/*
    ADnoteParameters.cpp - Parameters for ADnote (ADsynth)

    Original ZynAddSubFX author Nasca Octavian Paul
    Copyright (C) 2002-2005 Nasca Octavian Paul
    Copyright 2009-2011, Alan Calvert
    Copyright 2017-2019 Will Godfrey
    Copyright 2020-2021 Kristian Amlie, Will Godfrey

    This file is part of yoshimi, which is free software: you can redistribute
    it and/or modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either version 2 of
    the License, or (at your option) any later version.

    yoshimi 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 General Public License (version 2 or
    later) for more details.

    You should have received a copy of the GNU General Public License along with
    yoshimi; if not, write to the Free Software Foundation, Inc., 51 Franklin
    Street, Fifth Floor, Boston, MA  02110-1301, USA.

    This file is derivative of ZynAddSubFX original code.
*/

#include <iostream>

#include <cmath>
#include <stdlib.h>
#include "Misc/NumericFuncs.h"
#include "Misc/SynthEngine.h"
#include "Params/ADnoteParameters.h"

using func::setAllPan;
using func::power;


int ADnoteParameters::ADnote_unison_sizes[] =
{2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 25, 30, 40, 50, 0};

ADnoteParameters::ADnoteParameters(fft::Calc& fft_, SynthEngine *_synth) :
    Presets(_synth),
    fft(fft_)
{
    setpresettype("Padsyth");
    GlobalPar.FreqEnvelope = new EnvelopeParams(0, 0, synth);
    GlobalPar.FreqEnvelope->ASRinit(64, 50, 64, 60);
    GlobalPar.FreqLfo = new LFOParams(70, 0, 64, 0, 0, 0, 0, 0, synth);

    GlobalPar.AmpEnvelope = new EnvelopeParams(64, 1, synth);
    GlobalPar.AmpEnvelope->ADSRinit_dB(0, 40, 127, 25);
    GlobalPar.AmpLfo = new LFOParams(80, 0, 64, 0, 0, 0, 0, 1, synth);

    GlobalPar.GlobalFilter = new FilterParams(2, 94, 40, 0, synth);
    GlobalPar.FilterEnvelope = new EnvelopeParams(0, 1, synth);
    GlobalPar.FilterEnvelope->ADSRinit_filter(64, 40, 64, 70, 60, 64);
    GlobalPar.FilterLfo = new LFOParams(80, 0, 64, 0, 0, 0, 0, 2, synth);
    GlobalPar.Reson = new Resonance(synth);

    for (int nvoice = 0; nvoice < NUM_VOICES; ++nvoice)
        enableVoice(nvoice);
    defaults();
}


void ADnoteParameters::defaults(void)
{
    // Frequency Global Parameters
    GlobalPar.PStereo = true; // stereo
    GlobalPar.PDetune = 8192; // zero
    GlobalPar.PCoarseDetune = 0;
    GlobalPar.PDetuneType = 1;
    GlobalPar.FreqEnvelope->defaults();
    GlobalPar.FreqLfo->defaults();
    GlobalPar.PBandwidth = 64;

    // Amplitude Global Parameters
    GlobalPar.PVolume = 90;
    setGlobalPan(GlobalPar.PPanning = 64, synth->getRuntime().panLaw); // center
    GlobalPar.PAmpVelocityScaleFunction = 64;
    GlobalPar.PRandom = false;
    GlobalPar.PWidth = 63;
    GlobalPar.AmpEnvelope->defaults();
    GlobalPar.AmpLfo->defaults();
    GlobalPar.Fadein_adjustment = FADEIN_ADJUSTMENT_SCALE;
    GlobalPar.PPunchStrength = 0;
    GlobalPar.PPunchTime = 60;
    GlobalPar.PPunchStretch = 64;
    GlobalPar.PPunchVelocitySensing = 72;
    GlobalPar.Hrandgrouping = 0;

    // Filter Global Parameters
    GlobalPar.PFilterVelocityScale = 64;
    GlobalPar.PFilterVelocityScaleFunction = 64;
    GlobalPar.GlobalFilter->defaults();
    GlobalPar.FilterEnvelope->defaults();
    GlobalPar.FilterLfo->defaults();
    GlobalPar.Reson->defaults();

    for (int nvoice = 0; nvoice < NUM_VOICES; ++nvoice)
        defaults(nvoice);
    VoicePar[0].Enabled = 1;
}


// Defaults a voice
void ADnoteParameters::defaults(int n)
{
    int nvoice = n;
    VoicePar[nvoice].Enabled = 0;

    VoicePar[nvoice].Unison_size = 1;
    VoicePar[nvoice].Unison_frequency_spread = 60;
    VoicePar[nvoice].Unison_stereo_spread = 64;
    VoicePar[nvoice].Unison_vibrato = 64;
    VoicePar[nvoice].Unison_vibrato_speed = 64;
    VoicePar[nvoice].Unison_invert_phase = 0;
    VoicePar[nvoice].Unison_phase_randomness = 127;

    VoicePar[nvoice].Type = 0;
    VoicePar[nvoice].Pfixedfreq = 0;
    VoicePar[nvoice].PfixedfreqET = 0;
    VoicePar[nvoice].PBendAdjust = 88; // 64 + 24
    VoicePar[nvoice].POffsetHz     = 64;
    VoicePar[nvoice].Presonance = 1;
    VoicePar[nvoice].Pfilterbypass = 0;
    VoicePar[nvoice].Pextoscil = -1;
    VoicePar[nvoice].PextFMoscil = -1;
    VoicePar[nvoice].Poscilphase = 64;
    VoicePar[nvoice].PFMoscilphase = 64;
    VoicePar[nvoice].PDelay = 0;
    VoicePar[nvoice].PVolume = 100;
    VoicePar[nvoice].PVolumeminus = 0;
    setVoicePan(nvoice, VoicePar[nvoice].PPanning = 64, synth->getRuntime().panLaw); // center
    VoicePar[nvoice].PRandom = false;
    VoicePar[nvoice].PWidth = 63;
    VoicePar[nvoice].PDetune = 8192; // 8192 = 0
    VoicePar[nvoice].PCoarseDetune = 0;
    VoicePar[nvoice].PDetuneType = 0;
    VoicePar[nvoice].PFreqLfoEnabled = 0;
    VoicePar[nvoice].PFreqEnvelopeEnabled = 0;
    VoicePar[nvoice].PAmpEnvelopeEnabled = 0;
    VoicePar[nvoice].PAmpLfoEnabled = 0;
    VoicePar[nvoice].PAmpVelocityScaleFunction = 127;
    VoicePar[nvoice].PFilterEnabled = 0;
    VoicePar[nvoice].PFilterEnvelopeEnabled = 0;
    VoicePar[nvoice].PFilterLfoEnabled = 0;
    VoicePar[nvoice].PFilterVelocityScale = 0;
    VoicePar[nvoice].PFilterVelocityScaleFunction = 64;
    VoicePar[nvoice].PFMEnabled = 0;
    VoicePar[nvoice].PFMEnabled = 0;
    VoicePar[nvoice].PFMringToSide = false;
    VoicePar[nvoice].PFMFixedFreq = false;

    // I use the internal oscillator (-1)
    VoicePar[nvoice].PVoice = -1;
    VoicePar[nvoice].PFMVoice = -1;

    VoicePar[nvoice].PFMVolume = 90;
    VoicePar[nvoice].PFMVolumeDamp = 64;
    VoicePar[nvoice].PFMDetuneFromBaseOsc = 1;
    VoicePar[nvoice].PFMDetune = 8192;
    VoicePar[nvoice].PFMCoarseDetune = 0;
    VoicePar[nvoice].PFMDetuneType = 0;
    VoicePar[nvoice].PFMFreqEnvelopeEnabled = 0;
    VoicePar[nvoice].PFMAmpEnvelopeEnabled = 0;
    VoicePar[nvoice].PFMVelocityScaleFunction = 64;

    VoicePar[nvoice].POscil->defaults();
    VoicePar[nvoice].POscilFM->defaults();

    VoicePar[nvoice].AmpEnvelope->defaults();
    VoicePar[nvoice].AmpLfo->defaults();

    VoicePar[nvoice].FreqEnvelope->defaults();
    VoicePar[nvoice].FreqLfo->defaults();

    VoicePar[nvoice].VoiceFilter->defaults();
    VoicePar[nvoice].FilterEnvelope->defaults();
    VoicePar[nvoice].FilterLfo->defaults();

    VoicePar[nvoice].FMFreqEnvelope->defaults();
    VoicePar[nvoice].FMAmpEnvelope->defaults();
}


// Init the voice parameters
void ADnoteParameters::enableVoice(int nvoice)
{
    VoicePar[nvoice].POscil = new OscilParameters(fft, synth);
    VoicePar[nvoice].POscilFM = new OscilParameters(fft, synth);
    VoicePar[nvoice].OscilSmp = new OscilGen(fft, GlobalPar.Reson, synth, VoicePar[nvoice].POscil);
    VoicePar[nvoice].FMSmp = new OscilGen(fft, NULL, synth, VoicePar[nvoice].POscilFM);

    VoicePar[nvoice].AmpEnvelope = new EnvelopeParams(64, 1, synth);
    VoicePar[nvoice].AmpEnvelope->ADSRinit_dB(0, 100, 127, 100);
    VoicePar[nvoice].AmpLfo = new LFOParams(90, 32, 64, 0, 0, 30, 0, 1, synth);

    VoicePar[nvoice].FreqEnvelope = new EnvelopeParams(0, 0, synth);
    VoicePar[nvoice].FreqEnvelope->ASRinit(30, 40, 64, 60);
    VoicePar[nvoice].FreqLfo = new LFOParams(50, 40, 0, 0, 0, 0, 0, 0, synth);

    VoicePar[nvoice].VoiceFilter = new FilterParams(2, 50, 60, 0, synth);
    VoicePar[nvoice].FilterEnvelope = new EnvelopeParams(0, 0, synth);
    VoicePar[nvoice].FilterEnvelope->ADSRinit_filter(90, 70, 40, 70, 10, 40);
    VoicePar[nvoice].FilterLfo = new LFOParams(50, 20, 64, 0, 0, 0, 0, 2, synth);

    VoicePar[nvoice].FMFreqEnvelope = new EnvelopeParams(0, 0, synth);
    VoicePar[nvoice].FMFreqEnvelope->ASRinit(20, 90, 40, 80);
    VoicePar[nvoice].FMAmpEnvelope = new EnvelopeParams(64, 1, synth);
    VoicePar[nvoice].FMAmpEnvelope->ADSRinit(80, 90, 127, 100);
}


// Get the Multiplier of the fine detunes of the voices
float ADnoteParameters::getBandwidthDetuneMultiplier(void)
{
    float bw = (GlobalPar.PBandwidth - 64.0f) / 64.0f;
    bw = power<2>(bw * pow(fabs(bw), 0.2f) * 5.0f);
    return bw;
}


// Get the unison spread in cents for a voice
float ADnoteParameters::getUnisonFrequencySpreadCents(int nvoice)
{
    float unison_spread = VoicePar[nvoice].Unison_frequency_spread / 127.0f;
    unison_spread = powf(unison_spread * 2.0f, 2.0f) * 50.0f; // cents
    return unison_spread;
}


// Kill the voice
void ADnoteParameters::killVoice(int nvoice)
{
    delete VoicePar[nvoice].OscilSmp;
    delete VoicePar[nvoice].FMSmp;
    delete VoicePar[nvoice].POscil;
    delete VoicePar[nvoice].POscilFM;

    delete VoicePar[nvoice].AmpEnvelope;
    delete VoicePar[nvoice].AmpLfo;

    delete VoicePar[nvoice].FreqEnvelope;
    delete VoicePar[nvoice].FreqLfo;

    delete VoicePar[nvoice].VoiceFilter;
    delete VoicePar[nvoice].FilterEnvelope;
    delete VoicePar[nvoice].FilterLfo;

    delete VoicePar[nvoice].FMFreqEnvelope;
    delete VoicePar[nvoice].FMAmpEnvelope;
}


ADnoteParameters::~ADnoteParameters()
{
    delete GlobalPar.FreqEnvelope;
    delete GlobalPar.FreqLfo;
    delete GlobalPar.AmpEnvelope;
    delete GlobalPar.AmpLfo;
    delete GlobalPar.GlobalFilter;
    delete GlobalPar.FilterEnvelope;
    delete GlobalPar.FilterLfo;
    delete GlobalPar.Reson;

    for (int nvoice = 0; nvoice < NUM_VOICES; ++nvoice)
        killVoice(nvoice);
}


void ADnoteParameters::setGlobalPan(char pan, unsigned char panLaw)
{
    GlobalPar.PPanning = pan;
    if (!GlobalPar.PRandom)
        setAllPan(GlobalPar.PPanning, GlobalPar.pangainL, GlobalPar.pangainR, panLaw);
    else
        GlobalPar.pangainL = GlobalPar.pangainR = 0.7f;
}


void ADnoteParameters::setVoicePan(int nvoice, char pan, unsigned char panLaw)
{
    VoicePar[nvoice].PPanning = pan;
    if (!VoicePar[nvoice].PRandom)
        setAllPan(VoicePar[nvoice].PPanning, VoicePar[nvoice].pangainL, VoicePar[nvoice].pangainR, panLaw);
    else
        VoicePar[nvoice].pangainL = VoicePar[nvoice].pangainR = 0.7f;
}


void ADnoteParameters::add2XMLsection(XMLwrapper *xml, int n)
{
    int nvoice = n;
    if (nvoice >= NUM_VOICES)
        return;

    // currently not used
    // bool yoshiFormat = synth->usingYoshiType;
    int oscilused = 0, fmoscilused = 0; // if the oscil or fmoscil are used by another voice

    for (int i = 0; i < NUM_VOICES; ++i)
    {
        if (VoicePar[i].Pextoscil == nvoice)
            oscilused = 1;
        if (VoicePar[i].PextFMoscil == nvoice)
            fmoscilused = 1;
    }

    xml->addparbool("enabled", VoicePar[nvoice].Enabled);
    if (((VoicePar[nvoice].Enabled == 0) && (oscilused==0) && (fmoscilused==0)) && (xml->minimal)) return;

    xml->addpar("type", VoicePar[nvoice].Type);

    xml->addpar("unison_size",             VoicePar[nvoice].Unison_size);
    xml->addpar("unison_frequency_spread", VoicePar[nvoice].Unison_frequency_spread);
    xml->addpar("unison_stereo_spread",    VoicePar[nvoice].Unison_stereo_spread);
    xml->addpar("unison_vibratto",         VoicePar[nvoice].Unison_vibrato);
    xml->addpar("unison_vibratto_speed",   VoicePar[nvoice].Unison_vibrato_speed);
    xml->addpar("unison_invert_phase",     VoicePar[nvoice].Unison_invert_phase);
    xml->addpar("unison_phase_randomness", VoicePar[nvoice].Unison_phase_randomness);

    xml->addpar("delay", VoicePar[nvoice].PDelay);
    xml->addparbool("resonance", VoicePar[nvoice].Presonance);

    xml->addpar("input_voice", VoicePar[nvoice].PVoice);
    xml->addpar("ext_oscil",   VoicePar[nvoice].Pextoscil);
    xml->addpar("ext_fm_oscil",VoicePar[nvoice].PextFMoscil);

    xml->addpar("oscil_phase", VoicePar[nvoice].Poscilphase);
    xml->addpar("oscil_fm_phase", VoicePar[nvoice].PFMoscilphase);

    xml->addparbool("filter_enabled",VoicePar[nvoice].PFilterEnabled);
    xml->addparbool("filter_bypass", VoicePar[nvoice].Pfilterbypass);

    xml->addpar("fm_enabled", VoicePar[nvoice].PFMEnabled);

    xml->beginbranch("OSCIL");
        VoicePar[nvoice].POscil->add2XML(xml);
    xml->endbranch();


    xml->beginbranch("AMPLITUDE_PARAMETERS");
        // new yoshi type
        xml->addpar("pan_pos", VoicePar[nvoice].PPanning);
        xml->addparbool("random_pan", VoicePar[nvoice].PRandom);
        xml->addpar("random_width", VoicePar[nvoice].PWidth);

        // legacy
        if (VoicePar[nvoice].PRandom)
            xml->addpar("panning", 0);
        else
            xml->addpar("panning", VoicePar[nvoice].PPanning);

        xml->addpar("volume", VoicePar[nvoice].PVolume);
        xml->addparbool("volume_minus", VoicePar[nvoice].PVolumeminus);
        xml->addpar("velocity_sensing", VoicePar[nvoice].PAmpVelocityScaleFunction);
        xml->addparbool("amp_envelope_enabled", VoicePar[nvoice].PAmpEnvelopeEnabled);
        if ((VoicePar[nvoice].PAmpEnvelopeEnabled!=0) || (!xml->minimal))
        {
            xml->beginbranch("AMPLITUDE_ENVELOPE");
                VoicePar[nvoice].AmpEnvelope->add2XML(xml);
            xml->endbranch();
        }
        xml->addparbool("amp_lfo_enabled", VoicePar[nvoice].PAmpLfoEnabled);
        if ((VoicePar[nvoice].PAmpLfoEnabled != 0) || (!xml->minimal))
        {
            xml->beginbranch("AMPLITUDE_LFO");
                VoicePar[nvoice].AmpLfo->add2XML(xml);
            xml->endbranch();
        }
    xml->endbranch();

    xml->beginbranch("FREQUENCY_PARAMETERS");
        xml->addparbool("fixed_freq", VoicePar[nvoice].Pfixedfreq);
        xml->addpar("fixed_freq_et", VoicePar[nvoice].PfixedfreqET);
        xml->addpar("bend_adjust", VoicePar[nvoice].PBendAdjust);
        xml->addpar("offset_hz", VoicePar[nvoice].POffsetHz);
        xml->addpar("detune", VoicePar[nvoice].PDetune);
        xml->addpar("coarse_detune", VoicePar[nvoice].PCoarseDetune);
        xml->addpar("detune_type", VoicePar[nvoice].PDetuneType);

        xml->addparbool("freq_envelope_enabled", VoicePar[nvoice].PFreqEnvelopeEnabled);
        if ((VoicePar[nvoice].PFreqEnvelopeEnabled != 0) || (!xml->minimal))
        {
            xml->beginbranch("FREQUENCY_ENVELOPE");
                VoicePar[nvoice].FreqEnvelope->add2XML(xml);
            xml->endbranch();
        }
        xml->addparbool("freq_lfo_enabled", VoicePar[nvoice].PFreqLfoEnabled);
        if ((VoicePar[nvoice].PFreqLfoEnabled != 0) || (!xml->minimal))
        {
            xml->beginbranch("FREQUENCY_LFO");
                VoicePar[nvoice].FreqLfo->add2XML(xml);
            xml->endbranch();
        }
    xml->endbranch();

    if ((VoicePar[nvoice].PFilterEnabled != 0) || (!xml->minimal))
    {
        xml->beginbranch("FILTER_PARAMETERS");
            xml->addpar("velocity_sensing_amplitude", VoicePar[nvoice].PFilterVelocityScale);
            xml->addpar("velocity_sensing", VoicePar[nvoice].PFilterVelocityScaleFunction);
            xml->beginbranch("FILTER");
                VoicePar[nvoice].VoiceFilter->add2XML(xml);
            xml->endbranch();

            xml->addparbool("filter_envelope_enabled", VoicePar[nvoice].PFilterEnvelopeEnabled);
            if ((VoicePar[nvoice].PFilterEnvelopeEnabled != 0) || (!xml->minimal))
            {
                xml->beginbranch("FILTER_ENVELOPE");
                    VoicePar[nvoice].FilterEnvelope->add2XML(xml);
                xml->endbranch();
            }

            xml->addparbool("filter_lfo_enabled", VoicePar[nvoice].PFilterLfoEnabled);
            if ((VoicePar[nvoice].PFilterLfoEnabled !=0) || (!xml->minimal))
            {
                xml->beginbranch("FILTER_LFO");
                    VoicePar[nvoice].FilterLfo->add2XML(xml);
                xml->endbranch();
            }
            xml->endbranch();
    }

    if ((VoicePar[nvoice].PFMEnabled != 0) || (fmoscilused !=0 ) || (!xml->minimal))
    {
        xml->beginbranch("FM_PARAMETERS");
            xml->addpar("input_voice", VoicePar[nvoice].PFMVoice);

            xml->addpar("volume", VoicePar[nvoice].PFMVolume);
            xml->addpar("volume_damp", VoicePar[nvoice].PFMVolumeDamp);
            xml->addpar("velocity_sensing", VoicePar[nvoice].PFMVelocityScaleFunction);

            xml->addparbool("amp_envelope_enabled", VoicePar[nvoice].PFMAmpEnvelopeEnabled);
            if ((VoicePar[nvoice].PFMAmpEnvelopeEnabled != 0) || (!xml->minimal))
            {
                xml->beginbranch("AMPLITUDE_ENVELOPE");
                    VoicePar[nvoice].FMAmpEnvelope->add2XML(xml);
                xml->endbranch();
            }
            xml->beginbranch("MODULATOR");
                xml->addparbool("detune_from_base_osc", VoicePar[nvoice].PFMDetuneFromBaseOsc);
                xml->addpar("detune", VoicePar[nvoice].PFMDetune);
                xml->addpar("coarse_detune", VoicePar[nvoice].PFMCoarseDetune);
                xml->addpar("detune_type", VoicePar[nvoice].PFMDetuneType);

                xml->addparbool("freq_envelope_enabled", VoicePar[nvoice].PFMFreqEnvelopeEnabled);
                xml->addparbool("fixed_freq", VoicePar[nvoice].PFMFixedFreq);
                if ((VoicePar[nvoice].PFMFreqEnvelopeEnabled != 0) || (!xml->minimal))
                {
                    xml->beginbranch("FREQUENCY_ENVELOPE");
                        VoicePar[nvoice].FMFreqEnvelope->add2XML(xml);
                    xml->endbranch();
                }

                xml->beginbranch("OSCIL");
                    VoicePar[nvoice].POscilFM->add2XML(xml);
                xml->endbranch();

            xml->endbranch();
        xml->endbranch();
    }
}


void ADnoteParameters::add2XML(XMLwrapper *xml)
{
    // currently not used
    // bool yoshiFormat = synth->usingYoshiType;
    xml->information.ADDsynth_used = 1;

    xml->addparbool("stereo", GlobalPar.PStereo);

    xml->beginbranch("AMPLITUDE_PARAMETERS");
        xml->addpar("volume", GlobalPar.PVolume);
        // new yoshi type
        xml->addpar("pan_pos", GlobalPar.PPanning);
        xml->addparbool("random_pan", GlobalPar.PRandom);
        xml->addpar("random_width", GlobalPar.PWidth);

        // legacy
        if (GlobalPar.PRandom)
            xml->addpar("panning", 0);
        else
            xml->addpar("panning", GlobalPar.PPanning);

        xml->addpar("velocity_sensing", GlobalPar.PAmpVelocityScaleFunction);
        xml->addpar("fadein_adjustment", GlobalPar.Fadein_adjustment);
        xml->addpar("punch_strength", GlobalPar.PPunchStrength);
        xml->addpar("punch_time", GlobalPar.PPunchTime);
        xml->addpar("punch_stretch", GlobalPar.PPunchStretch);
        xml->addpar("punch_velocity_sensing", GlobalPar.PPunchVelocitySensing);
        xml->addpar("harmonic_randomness_grouping", GlobalPar.Hrandgrouping);

        xml->beginbranch("AMPLITUDE_ENVELOPE");
            GlobalPar.AmpEnvelope->add2XML(xml);
        xml->endbranch();

        xml->beginbranch("AMPLITUDE_LFO");
            GlobalPar.AmpLfo->add2XML(xml);
        xml->endbranch();
    xml->endbranch();

    xml->beginbranch("FREQUENCY_PARAMETERS");
        xml->addpar("detune", GlobalPar.PDetune);

        xml->addpar("coarse_detune", GlobalPar.PCoarseDetune);
        xml->addpar("detune_type", GlobalPar.PDetuneType);

        xml->addpar("bandwidth", GlobalPar.PBandwidth);

        xml->beginbranch("FREQUENCY_ENVELOPE");
            GlobalPar.FreqEnvelope->add2XML(xml);
        xml->endbranch();

        xml->beginbranch("FREQUENCY_LFO");
            GlobalPar.FreqLfo->add2XML(xml);
        xml->endbranch();
    xml->endbranch();


    xml->beginbranch("FILTER_PARAMETERS");
        xml->addpar("velocity_sensing_amplitude", GlobalPar.PFilterVelocityScale);
        xml->addpar("velocity_sensing", GlobalPar.PFilterVelocityScaleFunction);

        xml->beginbranch("FILTER");
            GlobalPar.GlobalFilter->add2XML(xml);
        xml->endbranch();

        xml->beginbranch("FILTER_ENVELOPE");
            GlobalPar.FilterEnvelope->add2XML(xml);
        xml->endbranch();

        xml->beginbranch("FILTER_LFO");
            GlobalPar.FilterLfo->add2XML(xml);
        xml->endbranch();
    xml->endbranch();

    xml->beginbranch("RESONANCE");
        GlobalPar.Reson->add2XML(xml);
    xml->endbranch();

    for (int nvoice=0;nvoice<NUM_VOICES;nvoice++)
    {
        xml->beginbranch("VOICE",nvoice);
        add2XMLsection(xml,nvoice);
        xml->endbranch();
    }
}


void ADnoteParameters::getfromXML(XMLwrapper *xml)
{
    GlobalPar.PStereo = (xml->getparbool("stereo", GlobalPar.PStereo)) != 0;

    if (xml->enterbranch("AMPLITUDE_PARAMETERS"))
    {
        GlobalPar.PVolume = xml->getpar127("volume", GlobalPar.PVolume);
        int test = xml->getpar127("random_width", UNUSED);
        if (test < 64) // new Yoshi type
        {
            GlobalPar.PWidth = test;
            setGlobalPan(xml->getpar127("pan_pos", GlobalPar.PPanning), synth->getRuntime().panLaw);
            GlobalPar.PRandom = xml->getparbool("random_pan", GlobalPar.PRandom);
        }
        else // legacy
        {
            setGlobalPan(xml->getpar127("panning", GlobalPar.PPanning), synth->getRuntime().panLaw);

            if (GlobalPar.PPanning == 0)
            {
                GlobalPar.PPanning = 64;
                GlobalPar.PRandom = true;
                GlobalPar.PWidth = 63;
            }
            else
                GlobalPar.PRandom = false;
        }

        GlobalPar.PAmpVelocityScaleFunction =
            xml->getpar127("velocity_sensing", GlobalPar.PAmpVelocityScaleFunction);
        GlobalPar.Fadein_adjustment = xml->getpar127("fadein_adjustment", GlobalPar.Fadein_adjustment);
        GlobalPar.PPunchStrength =
            xml->getpar127("punch_strength", GlobalPar.PPunchStrength);
        GlobalPar.PPunchTime = xml->getpar127("punch_time", GlobalPar.PPunchTime);
        GlobalPar.PPunchStretch = xml->getpar127("punch_stretch", GlobalPar.PPunchStretch);
        GlobalPar.PPunchVelocitySensing =
            xml->getpar127("punch_velocity_sensing", GlobalPar.PPunchVelocitySensing);
        GlobalPar.Hrandgrouping =
            xml->getpar127("harmonic_randomness_grouping", GlobalPar.Hrandgrouping);

        if (xml->enterbranch("AMPLITUDE_ENVELOPE"))
        {
            GlobalPar.AmpEnvelope->getfromXML(xml);
            xml->exitbranch();
        }

        if (xml->enterbranch("AMPLITUDE_LFO"))
        {
            GlobalPar.AmpLfo->getfromXML(xml);
            xml->exitbranch();
        }

        xml->exitbranch();
    }

    if (xml->enterbranch("FREQUENCY_PARAMETERS"))
    {
        GlobalPar.PDetune = xml->getpar("detune", GlobalPar.PDetune, 0, 16383);
        GlobalPar.PCoarseDetune =
            xml->getpar("coarse_detune", GlobalPar.PCoarseDetune, 0, 16383);
        GlobalPar.PDetuneType =
            xml->getpar127("detune_type", GlobalPar.PDetuneType);

        GlobalPar.PBandwidth = xml->getpar127("bandwidth", GlobalPar.PBandwidth);

        xml->enterbranch("FREQUENCY_ENVELOPE");
            GlobalPar.FreqEnvelope->getfromXML(xml);
        xml->exitbranch();

        xml->enterbranch("FREQUENCY_LFO");
            GlobalPar.FreqLfo->getfromXML(xml);
        xml->exitbranch();

        xml->exitbranch();
    }


    if (xml->enterbranch("FILTER_PARAMETERS"))
    {
        GlobalPar.PFilterVelocityScale =
            xml->getpar127("velocity_sensing_amplitude", GlobalPar.PFilterVelocityScale);
        GlobalPar.PFilterVelocityScaleFunction =
            xml->getpar127("velocity_sensing", GlobalPar.PFilterVelocityScaleFunction);

        xml->enterbranch("FILTER");
            GlobalPar.GlobalFilter->getfromXML(xml);
        xml->exitbranch();

        xml->enterbranch("FILTER_ENVELOPE");
            GlobalPar.FilterEnvelope->getfromXML(xml);
        xml->exitbranch();

        xml->enterbranch("FILTER_LFO");
            GlobalPar.FilterLfo->getfromXML(xml);
        xml->exitbranch();

        xml->exitbranch();
    }

    if (xml->enterbranch("RESONANCE"))
    {
        GlobalPar.Reson->getfromXML(xml);
        xml->exitbranch();
    }

    for (int nvoice = 0; nvoice < NUM_VOICES; nvoice++)
    {
        VoicePar[nvoice].Enabled=0;
        if (xml->enterbranch("VOICE", nvoice) == 0)
            continue;
        getfromXMLsection(xml, nvoice);
        xml->exitbranch();
    }
}

void ADnoteParameters::getfromXMLsection(XMLwrapper *xml, int n)
{
    int nvoice = n;
    if (nvoice >= NUM_VOICES) return;

    VoicePar[nvoice].Enabled = xml->getparbool("enabled", 0);

    VoicePar[nvoice].Unison_size =
        xml->getpar127("unison_size", VoicePar[nvoice].Unison_size);
    VoicePar[nvoice].Unison_frequency_spread =
        xml->getpar127("unison_frequency_spread", VoicePar[nvoice].Unison_frequency_spread);
    VoicePar[nvoice].Unison_stereo_spread =
        xml->getpar127("unison_stereo_spread", VoicePar[nvoice].Unison_stereo_spread);
    VoicePar[nvoice].Unison_vibrato =
        xml->getpar127("unison_vibratto", VoicePar[nvoice].Unison_vibrato);
    VoicePar[nvoice].Unison_vibrato_speed =
        xml->getpar127("unison_vibratto_speed", VoicePar[nvoice].Unison_vibrato_speed);
    VoicePar[nvoice].Unison_invert_phase =
        xml->getpar127("unison_invert_phase", VoicePar[nvoice].Unison_invert_phase);
    VoicePar[nvoice].Unison_phase_randomness =
        xml->getpar127("unison_phase_randomness", VoicePar[nvoice].Unison_phase_randomness);

    VoicePar[nvoice].Type = xml->getpar127("type", VoicePar[nvoice].Type);
    VoicePar[nvoice].PDelay = xml->getpar127("delay", VoicePar[nvoice].PDelay);
    VoicePar[nvoice].Presonance = xml->getparbool("resonance", VoicePar[nvoice].Presonance);

    VoicePar[nvoice].PVoice =
        xml->getpar("input_voice", VoicePar[nvoice].PVoice, -1, nvoice - 1);
    VoicePar[nvoice].Pextoscil = xml->getpar("ext_oscil", -1, -1, nvoice - 1);
    VoicePar[nvoice].PextFMoscil = xml->getpar("ext_fm_oscil", -1, -1,nvoice - 1);

    VoicePar[nvoice].Poscilphase =
        xml->getpar127("oscil_phase", VoicePar[nvoice].Poscilphase);
    VoicePar[nvoice].PFMoscilphase =
        xml->getpar127("oscil_fm_phase", VoicePar[nvoice].PFMoscilphase);

    VoicePar[nvoice].PFilterEnabled =
        xml->getparbool("filter_enabled",VoicePar[nvoice].PFilterEnabled);
    VoicePar[nvoice].Pfilterbypass =
        xml->getparbool("filter_bypass",VoicePar[nvoice].Pfilterbypass);

    VoicePar[nvoice].PFMEnabled = xml->getpar127("fm_enabled",VoicePar[nvoice].PFMEnabled);

    if (xml->enterbranch("OSCIL"))
    {
        VoicePar[nvoice].POscil->getfromXML(xml);
        xml->exitbranch();
    }

    if (xml->enterbranch("AMPLITUDE_PARAMETERS"))
    {
        int test = xml->getpar127("random_width", UNUSED);
        if (test < 64) // new Yoshi type
        {
            VoicePar[nvoice].PWidth = test;
            setVoicePan(nvoice, xml->getpar127("pan_pos", VoicePar[nvoice].PPanning), synth->getRuntime().panLaw);
            VoicePar[nvoice].PRandom = xml->getparbool("random_pan", VoicePar[nvoice].PRandom);
        }
        else  // legacy
        {
            setVoicePan(nvoice, xml->getpar127("panning", VoicePar[nvoice].PPanning), synth->getRuntime().panLaw);
            if (VoicePar[nvoice].PPanning == 0)
            {
                VoicePar[nvoice].PPanning = 64;
                VoicePar[nvoice].PRandom = true;
                VoicePar[nvoice].PWidth = 63;
            }
        }

        VoicePar[nvoice].PVolume = xml->getpar127("volume", VoicePar[nvoice].PVolume);
        VoicePar[nvoice].PVolumeminus =
            xml->getparbool("volume_minus", VoicePar[nvoice].PVolumeminus);
        VoicePar[nvoice].PAmpVelocityScaleFunction =
            xml->getpar127("velocity_sensing", VoicePar[nvoice].PAmpVelocityScaleFunction);

        VoicePar[nvoice].PAmpEnvelopeEnabled =
            xml->getparbool("amp_envelope_enabled",VoicePar[nvoice].PAmpEnvelopeEnabled);
        if (xml->enterbranch("AMPLITUDE_ENVELOPE"))
        {
            VoicePar[nvoice].AmpEnvelope->getfromXML(xml);
            xml->exitbranch();
        }

        VoicePar[nvoice].PAmpLfoEnabled =
            xml->getparbool("amp_lfo_enabled",VoicePar[nvoice].PAmpLfoEnabled);
        if (xml->enterbranch("AMPLITUDE_LFO"))
        {
            VoicePar[nvoice].AmpLfo->getfromXML(xml);
            xml->exitbranch();
        }
        xml->exitbranch();
    }

    if (xml->enterbranch("FREQUENCY_PARAMETERS"))
    {
        VoicePar[nvoice].Pfixedfreq =
            xml->getparbool("fixed_freq", VoicePar[nvoice].Pfixedfreq);
        VoicePar[nvoice].PfixedfreqET =
            xml->getpar127("fixed_freq_et", VoicePar[nvoice].PfixedfreqET);
        VoicePar[nvoice].PBendAdjust =
            xml->getpar127("bend_adjust", VoicePar[nvoice].PBendAdjust);
        VoicePar[nvoice].POffsetHz =
            xml->getpar127("offset_hz", VoicePar[nvoice].POffsetHz);


        VoicePar[nvoice].PDetune =
            xml->getpar("detune", VoicePar[nvoice].PDetune, 0, 16383);

        VoicePar[nvoice].PCoarseDetune =
            xml->getpar("coarse_detune", VoicePar[nvoice].PCoarseDetune, 0, 16383);
        VoicePar[nvoice].PDetuneType =
            xml->getpar127("detune_type", VoicePar[nvoice].PDetuneType);

        VoicePar[nvoice].PFreqEnvelopeEnabled =
            xml->getparbool("freq_envelope_enabled", VoicePar[nvoice].PFreqEnvelopeEnabled);
        if (xml->enterbranch("FREQUENCY_ENVELOPE"))
        {
            VoicePar[nvoice].FreqEnvelope->getfromXML(xml);
            xml->exitbranch();
        }

        VoicePar[nvoice].PFreqLfoEnabled =
            xml->getparbool("freq_lfo_enabled", VoicePar[nvoice].PFreqLfoEnabled);
        if (xml->enterbranch("FREQUENCY_LFO"))
        {
            VoicePar[nvoice].FreqLfo->getfromXML(xml);
            xml->exitbranch();
        }
        xml->exitbranch();
    }

    if (xml->enterbranch("FILTER_PARAMETERS"))
    {
        VoicePar[nvoice].PFilterVelocityScale =
            xml->getpar127("velocity_sensing_amplitude",
            VoicePar[nvoice].PFilterVelocityScale);
        VoicePar[nvoice].PFilterVelocityScaleFunction =
            xml->getpar127("velocity_sensing",
            VoicePar[nvoice].PFilterVelocityScaleFunction);

        if (xml->enterbranch("FILTER"))
        {
            VoicePar[nvoice].VoiceFilter->getfromXML(xml);
            xml->exitbranch();
        }

        VoicePar[nvoice].PFilterEnvelopeEnabled =
            xml->getparbool("filter_envelope_enabled", VoicePar[nvoice].PFilterEnvelopeEnabled);
        if (xml->enterbranch("FILTER_ENVELOPE"))
        {
            VoicePar[nvoice].FilterEnvelope->getfromXML(xml);
            xml->exitbranch();
        }

        VoicePar[nvoice].PFilterLfoEnabled =
            xml->getparbool("filter_lfo_enabled", VoicePar[nvoice].PFilterLfoEnabled);
        if (xml->enterbranch("FILTER_LFO"))
        {
            VoicePar[nvoice].FilterLfo->getfromXML(xml);
            xml->exitbranch();
        }
        xml->exitbranch();
    }

    if (xml->enterbranch("FM_PARAMETERS"))
    {
        VoicePar[nvoice].PFMVoice =
            xml->getpar("input_voice", VoicePar[nvoice].PFMVoice, -1, nvoice - 1);

        VoicePar[nvoice].PFMVolume = xml->getpar127("volume", VoicePar[nvoice].PFMVolume);
        VoicePar[nvoice].PFMVolumeDamp =
            xml->getpar127("volume_damp", VoicePar[nvoice].PFMVolumeDamp);
        VoicePar[nvoice].PFMVelocityScaleFunction =
            xml->getpar127("velocity_sensing", VoicePar[nvoice].PFMVelocityScaleFunction);

        VoicePar[nvoice].PFMAmpEnvelopeEnabled =
            xml->getparbool("amp_envelope_enabled", VoicePar[nvoice].PFMAmpEnvelopeEnabled);
        if (xml->enterbranch("AMPLITUDE_ENVELOPE"))
        {
            VoicePar[nvoice].FMAmpEnvelope->getfromXML(xml);
            xml->exitbranch();
        }

        if (xml->enterbranch("MODULATOR"))
        {
            bool loadFMFreqParams = true;
            VoicePar[nvoice].PFMDetuneFromBaseOsc =
                xml->getparbool("detune_from_base_osc", 127);
            if (VoicePar[nvoice].PFMDetuneFromBaseOsc == 127) {
                // In the past it was not possible to choose whether to include
                // detuning from the base oscillator. For local modulators it
                // was always enabled, for imported voice modulators it was
                // always disabled. To load old patches correctly, we apply this
                // old behavior here if the XML element is missing from the
                // patch. New patches will always save one or the other.
                //
                // In a similar fashion, it was not possible to apply frequency
                // parameters to imported voice modulators in the past, however
                // it was possible to save them if you edited them before
                // switching to an imported voice. Now that frequency parameters
                // are respected, we need to ignore those parameters for old
                // instruments that saved them, but didn't use them, otherwise
                // the instrument will sound different.
                if (VoicePar[nvoice].PFMVoice >= 0) {
                    VoicePar[nvoice].PFMDetuneFromBaseOsc = 0;
                    loadFMFreqParams = false;

                    // In the past the fixed frequency of the imported voice was
                    // respected. Now, the fixed frequency of the modulator is
                    // respected. So if we load an old patch, fetch that setting
                    // from the imported voice.
                    VoicePar[nvoice].PFMFixedFreq =
                        VoicePar[VoicePar[nvoice].PFMVoice].Pfixedfreq;
                } else {
                    VoicePar[nvoice].PFMDetuneFromBaseOsc = 1;
                }
            }
            if (loadFMFreqParams) {
                VoicePar[nvoice].PFMDetune =
                    xml->getpar("detune",VoicePar[nvoice].PFMDetune, 0, 16383);
                VoicePar[nvoice].PFMCoarseDetune =
                    xml->getpar("coarse_detune", VoicePar[nvoice].PFMCoarseDetune, 0, 16383);
                VoicePar[nvoice].PFMDetuneType =
                    xml->getpar127("detune_type", VoicePar[nvoice].PFMDetuneType);

                VoicePar[nvoice].PFMFreqEnvelopeEnabled =
                    xml->getparbool("freq_envelope_enabled",
                                    VoicePar[nvoice].PFMFreqEnvelopeEnabled);
                VoicePar[nvoice].PFMFixedFreq = xml->getparbool("fixed_freq", VoicePar[nvoice].PFMFixedFreq);
                if (xml->enterbranch("FREQUENCY_ENVELOPE"))
                {
                    VoicePar[nvoice].FMFreqEnvelope->getfromXML(xml);
                    xml->exitbranch();
                }
            }

            if (xml->enterbranch("OSCIL"))
            {
                VoicePar[nvoice].POscilFM->getfromXML(xml);
                xml->exitbranch();
            }

            xml->exitbranch();
        }
        xml->exitbranch();
    }
}


float ADnoteParameters::getLimits(CommandBlock *getData)
{
    float value = getData->data.value;
    int request = int(getData->data.type & TOPLEVEL::type::Default);
    int control = getData->data.control;
    int engine = getData->data.engine;

    unsigned char type = 0;

    // addnote defaults
    int min = 0;
    float def = 0;
    int max = 127;
    type |= TOPLEVEL::type::Integer;
    unsigned char learnable = TOPLEVEL::type::Learnable;

    if (engine == PART::engine::addSynth)
    {
        switch (control)
        {
            case ADDSYNTH::control::volume:
                type |= learnable;
                def = 90;
                break;

            case ADDSYNTH::control::velocitySense:
                type |= learnable;
                def = 64;
                break;

            case ADDSYNTH::control::panning:
                type |= learnable;
                def = 64;
                break;

            case ADDSYNTH::control::enableRandomPan:
                max = 1;
                break;

            case ADDSYNTH::control::randomWidth:
                type |= learnable;
                def = 63;
                max = 63;
                break;

            case ADDSYNTH::control::detuneFrequency:
                type |= learnable;
                min = -8192;
                max = 8191;
                break;

            case ADDSYNTH::control::octave:
                type |= learnable;
                min = -8;
                max = 7;
                break;

            case ADDSYNTH::control::detuneType:
                min = 1;
                max = 4;
                break;

            case ADDSYNTH::control::coarseDetune:
                min = -64;
                max = 63;
                break;

            case ADDSYNTH::control::relativeBandwidth:
                type |= learnable;
                def = 64;
                break;

            case ADDSYNTH::control::stereo:
                type |= learnable;
                def = 1;
                max = 1;
                break;

            case ADDSYNTH::control::randomGroup:
                max = 1;
                break;

            case ADDSYNTH::control::dePop:
                type |= learnable;
                def = FADEIN_ADJUSTMENT_SCALE;
                break;

            case ADDSYNTH::control::punchStrength:
                type |= learnable;
                break;

            case ADDSYNTH::control::punchDuration:
                type |= learnable;
                def = 60;
                break;

            case ADDSYNTH::control::punchStretch:
                type |= learnable;
                def = 64;
                break;

            case ADDSYNTH::control::punchVelocity:
                type |= learnable;
                def = 72;
                break;

            default:
                type |= TOPLEVEL::type::Error;
                break;
        }
        getData->data.type = type;
        if (type & TOPLEVEL::type::Error)
            return 1;

        switch (request)
        {
            case TOPLEVEL::type::Adjust:
                if (value < min)
                    value = min;
                else if (value > max)
                    value = max;
            break;
            case TOPLEVEL::type::Minimum:
                value = min;
                break;
            case TOPLEVEL::type::Maximum:
                value = max;
                break;
            case TOPLEVEL::type::Default:
                value = def;
                break;
        }
        return value;
    }

    switch (control)
    {
        case ADDVOICE::control::volume:
            type |= learnable;
            def = 100;
            break;

        case ADDVOICE::control::velocitySense:
            type |= learnable;
            def = 127;
            break;

        case ADDVOICE::control::panning:
            type |= learnable;
            def = 64;
            break;
        case ADDVOICE::control::enableRandomPan:
            max = 1;
            break;

        case ADDVOICE::control::randomWidth:
            def = 63;
            max = 63;
            break;

        case ADDVOICE::control::invertPhase:
            max = 1;
            break;

        case ADDVOICE::control::enableAmplitudeEnvelope:
            type |= learnable;
            max = 1;
            break;
        case ADDVOICE::control::enableAmplitudeLFO:
            type |= learnable;
            max = 1;
            break;

        case ADDVOICE::control::modulatorType:
            type |= learnable;
            max = 5;
            break;

        case ADDVOICE::control::externalModulator:
            min = -1;
            def = -1;
            max = 6;
            break;

        case ADDVOICE::control::externalOscillator:
            min = -1;
            def = -1;
            max = 6;
            break;

        case ADDVOICE::control::detuneFrequency:
            type |= learnable;
            min = -8192;
            max = 8191;
            break;

        case ADDVOICE::control::equalTemperVariation:
            type |= learnable;
            break;

        case ADDVOICE::control::baseFrequencyAs440Hz:
            max = 1;
            break;

        case ADDVOICE::control::octave:
            type |= learnable;
            min = -8;
            max = 7;
            break;

        case ADDVOICE::control::detuneType:
            max = 4;
            break;

        case ADDVOICE::control::coarseDetune:
            min = -64;
            max = 63;
            break;

        case ADDVOICE::control::pitchBendAdjustment:
            type |= learnable;
            def = 88;
            break;

        case ADDVOICE::control::pitchBendOffset:
            type |= learnable;
            def = 64;
            break;

        case ADDVOICE::control::enableFrequencyEnvelope:
            type |= learnable;
            max = 1;
            break;
        case ADDVOICE::control::enableFrequencyLFO:
            type |= learnable;
            max = 1;
            break;

        case ADDVOICE::control::unisonFrequencySpread:
            type |= learnable;
            def = 60;
            break;

        case ADDVOICE::control::unisonPhaseRandomise:
            type |= learnable;
            def = 127;
            break;

        case ADDVOICE::control::unisonStereoSpread:
            type |= learnable;
            def = 64;
            break;

        case ADDVOICE::control::unisonVibratoDepth:
            type |= learnable;
            def = 64;
            break;

        case ADDVOICE::control::unisonVibratoSpeed:
            type |= learnable;
            def = 64;
            break;

        case ADDVOICE::control::unisonSize:
            min = 2;
            def = 2;
            max = 50;
            break;

        case ADDVOICE::control::unisonPhaseInvert:
            max = 5;
            break;

        case ADDVOICE::control::enableUnison:
            type |= learnable;
            max = 1;
            break;

        case ADDVOICE::control::bypassGlobalFilter:
            max = 1;
            break;

        case ADDVOICE::control::enableFilter:
            type |= learnable;
            max = 1;
            break;
        case ADDVOICE::control::enableFilterEnvelope:
            type |= learnable;
            max = 1;
            break;
        case ADDVOICE::control::enableFilterLFO:
            type |= learnable;
            max = 1;
            break;

        case ADDVOICE::control::modulatorAmplitude:
            type |= learnable;
            def = 90;
            break;

        case ADDVOICE::control::modulatorVelocitySense:
            type |= learnable;
            def = 64;
            break;

        case ADDVOICE::control::modulatorHFdamping:
            type |= learnable;
            min = -64;
            max = 63;
            break;
        case ADDVOICE::control::enableModulatorAmplitudeEnvelope:
            type |= learnable;
            max = 1;
            break;
        case ADDVOICE::control::modulatorDetuneFrequency:
            type |= learnable;
            min = -8192;
            max = 8191;
            break;
        case ADDVOICE::control::modulatorDetuneFromBaseOsc:
            def = 1;
            max = 1;
            break;
        case ADDVOICE::control::modulatorFrequencyAs440Hz:
            max = 1;
            break;
        case ADDVOICE::control::modulatorOctave:
            type |= learnable;
            min = -8;
            max = 7;
            break;
        case ADDVOICE::control::modulatorDetuneType:
            max = 4;
            break;
        case ADDVOICE::control::modulatorCoarseDetune:
            min = -64;
            max = 63;
            break;
        case ADDVOICE::control::enableModulatorFrequencyEnvelope:
            type |= learnable;
            max = 1;
            break;
        case ADDVOICE::control::modulatorOscillatorPhase:
            type |= learnable;
            min = -64;
            max = 63;
            break;
        case ADDVOICE::control::modulatorOscillatorSource:
            min = -1;
            def = -1;
            max = 6;
            break;

        case ADDVOICE::control::delay:
            type |= learnable;
            break;
        case ADDVOICE::control::enableVoice:
            type |= learnable;
            if (engine == PART::engine::addVoice1)
                def = 1;
            max = 1;
            break;
        case ADDVOICE::control::enableResonance:
            def = 1;
            max = 1;
            break;

        case ADDVOICE::control::voiceOscillatorPhase:
            type |= learnable;
            min = -64;
            max = 63;
            break;
        case ADDVOICE::control::voiceOscillatorSource:
            min = -1;
            def = -1;
            max = 6;
            break;

        case ADDVOICE::control::soundType:
            max = 3;
            break;

        default:
            type |= TOPLEVEL::type::Error;
            break;
    }
    getData->data.type = type;
    if (type & TOPLEVEL::type::Error)
        return 1;

    switch (request)
    {
        case TOPLEVEL::type::Adjust:
            if (value < min)
                value = min;
            else if (value > max)
                value = max;
        break;
        case TOPLEVEL::type::Minimum:
            value = min;
            break;
        case TOPLEVEL::type::Maximum:
            value = max;
            break;
        case TOPLEVEL::type::Default:
            value = def;
            break;
    }
    return value;
}
