/*
  ZynAddSubFX - a software synthesizer

  Part.cpp - Part implementation
  Copyright (C) 2002-2005 Nasca Octavian Paul
  Author: Nasca Octavian Paul

  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License
  as published by the Free Software Foundation.

  This program 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 (version 2)
  along with this program; if not, write to the Free Software Foundation,
  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

*/

#include "Part.h"
#include "Microtonal.h"
#include "Util.h"
#include "XMLwrapper.h"
#include "../Effects/EffectMgr.h"
#include "../Params/ADnoteParameters.h"
#include "../Params/SUBnoteParameters.h"
#include "../Params/PADnoteParameters.h"
#include "../Synth/ADnote.h"
#include "../Synth/SUBnote.h"
#include "../Synth/PADnote.h"
#include "../DSP/FFTwrapper.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

Part::Part(Microtonal *microtonal_, FFTwrapper *fft_, pthread_mutex_t *mutex_)
{
    microtonal = microtonal_;
    fft      = fft_;
    mutex    = mutex_;
    pthread_mutex_init(&load_mutex, NULL);
    partoutl = new float [synth->buffersize];
    partoutr = new float [synth->buffersize];

    for(int n = 0; n < NUM_KIT_ITEMS; ++n) {
        kit[n].Pname   = new unsigned char [PART_MAX_NAME_LEN];
        kit[n].adpars  = NULL;
        kit[n].subpars = NULL;
        kit[n].padpars = NULL;
    }

    kit[0].adpars  = new ADnoteParameters(fft);
    kit[0].subpars = new SUBnoteParameters();
    kit[0].padpars = new PADnoteParameters(fft, mutex);

    //Part's Insertion Effects init
    for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) {
        partefx[nefx]    = new EffectMgr(1, mutex);
        Pefxbypass[nefx] = false;
    }

    for(int n = 0; n < NUM_PART_EFX + 1; ++n) {
        partfxinputl[n] = new float [synth->buffersize];
        partfxinputr[n] = new float [synth->buffersize];
    }

    killallnotes = 0;
    oldfreq      = -1.0f;

    for(int i = 0; i < POLIPHONY; ++i) {
        partnote[i].status = KEY_OFF;
        partnote[i].note   = -1;
        partnote[i].itemsplaying = 0;
        for(int j = 0; j < NUM_KIT_ITEMS; ++j) {
            partnote[i].kititem[j].adnote  = NULL;
            partnote[i].kititem[j].subnote = NULL;
            partnote[i].kititem[j].padnote = NULL;
        }
        partnote[i].time = 0;
    }
    cleanup();

    Pname = new unsigned char [PART_MAX_NAME_LEN];

    oldvolumel = oldvolumer = 0.5f;
    lastnote   = -1;
    lastpos    = 0; // lastpos will store previously used NoteOn(...)'s pos.
    lastlegatomodevalid = false; // To store previous legatomodevalid value.

    defaults();
}

void Part::defaults()
{
    Penabled    = 0;
    Pminkey     = 0;
    Pmaxkey     = 127;
    Pnoteon     = 1;
    Ppolymode   = 1;
    Plegatomode = 0;
    setPvolume(96);
    Pkeyshift = 64;
    Prcvchn   = 0;
    setPpanning(64);
    Pvelsns   = 64;
    Pveloffs  = 64;
    Pkeylimit = 15;
    defaultsinstrument();
    ctl.defaults();
}

void Part::defaultsinstrument()
{
    ZERO(Pname, PART_MAX_NAME_LEN);

    info.Ptype = 0;
    ZERO(info.Pauthor, MAX_INFO_TEXT_SIZE + 1);
    ZERO(info.Pcomments, MAX_INFO_TEXT_SIZE + 1);

    Pkitmode  = 0;
    Pdrummode = 0;

    for(int n = 0; n < NUM_KIT_ITEMS; ++n) {
        kit[n].Penabled    = 0;
        kit[n].Pmuted      = 0;
        kit[n].Pminkey     = 0;
        kit[n].Pmaxkey     = 127;
        kit[n].Padenabled  = 0;
        kit[n].Psubenabled = 0;
        kit[n].Ppadenabled = 0;
        ZERO(kit[n].Pname, PART_MAX_NAME_LEN);
        kit[n].Psendtoparteffect = 0;
        if(n != 0)
            setkititemstatus(n, 0);
    }
    kit[0].Penabled   = 1;
    kit[0].Padenabled = 1;
    kit[0].adpars->defaults();
    kit[0].subpars->defaults();
    kit[0].padpars->defaults();

    for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) {
        partefx[nefx]->defaults();
        Pefxroute[nefx] = 0; //route to next effect
    }
}



/*
 * Cleanup the part
 */
void Part::cleanup(bool final)
{
    for(int k = 0; k < POLIPHONY; ++k)
        KillNotePos(k);
    for(int i = 0; i < synth->buffersize; ++i) {
        partoutl[i] = final ? 0.0f : denormalkillbuf[i];
        partoutr[i] = final ? 0.0f : denormalkillbuf[i];
    }
    ctl.resetall();
    for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx)
        partefx[nefx]->cleanup();
    for(int n = 0; n < NUM_PART_EFX + 1; ++n)
        for(int i = 0; i < synth->buffersize; ++i) {
            partfxinputl[n][i] = final ? 0.0f : denormalkillbuf[i];
            partfxinputr[n][i] = final ? 0.0f : denormalkillbuf[i];
        }
}

Part::~Part()
{
    cleanup(true);
    for(int n = 0; n < NUM_KIT_ITEMS; ++n) {
        if(kit[n].adpars != NULL)
            delete (kit[n].adpars);
        if(kit[n].subpars != NULL)
            delete (kit[n].subpars);
        if(kit[n].padpars != NULL)
            delete (kit[n].padpars);
        kit[n].adpars  = NULL;
        kit[n].subpars = NULL;
        kit[n].padpars = NULL;
        delete [] kit[n].Pname;
    }

    delete [] Pname;
    delete [] partoutl;
    delete [] partoutr;
    for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx)
        delete (partefx[nefx]);
    for(int n = 0; n < NUM_PART_EFX + 1; ++n) {
        delete [] partfxinputl[n];
        delete [] partfxinputr[n];
    }
}

/*
 * Note On Messages
 */
void Part::NoteOn(unsigned char note,
                  unsigned char velocity,
                  int masterkeyshift)
{
    int i, pos;

    // Legato and MonoMem used vars:
    int  posb = POLIPHONY - 1; // Just a dummy initial value.
    bool legatomodevalid = false; //true when legato mode is determined applicable.
    bool doinglegato     = false; // true when we determined we do a legato note.
    bool ismonofirstnote = false; /*(In Mono/Legato) true when we determined
                  no other notes are held down or sustained.*/
    int lastnotecopy     = lastnote; //Useful after lastnote has been changed.

    if(Pnoteon == 0)
        return;
    if((note < Pminkey) || (note > Pmaxkey))
        return;

    // MonoMem stuff:
    if(Ppolymode == 0) { // If Poly is off
        monomemnotes.push_back(note); // Add note to the list.
        monomem[note].velocity  = velocity; // Store this note's velocity.
        monomem[note].mkeyshift = masterkeyshift; /* Store masterkeyshift too,
                         I'm not sure why though... */
        if((partnote[lastpos].status != KEY_PLAYING)
           && (partnote[lastpos].status != KEY_RELASED_AND_SUSTAINED))
            ismonofirstnote = true;  // No other keys are held or sustained.
    }
    else
    // Poly mode is On so just make sure the list is empty.
    if(not monomemnotes.empty())
        monomemnotes.clear();

    lastnote = note;

    pos = -1;
    for(i = 0; i < POLIPHONY; ++i)
        if(partnote[i].status == KEY_OFF) {
            pos = i;
            break;
        }

    if((Plegatomode != 0) && (Pdrummode == 0)) {
        if(Ppolymode != 0) {
            fprintf(
                stderr,
                "ZynAddSubFX WARNING: Poly and Legato modes are both On, that should not happen ! ... Disabling Legato mode ! - (Part.cpp::NoteOn(..))\n");
            Plegatomode = 0;
        }
        else {
            // Legato mode is on and applicable.
            legatomodevalid = true;
            if((not ismonofirstnote) && (lastlegatomodevalid)) {
                // At least one other key is held or sustained, and the
                // previous note was played while in valid legato mode.
                doinglegato = true; // So we'll do a legato note.
                pos  = lastpos; // A legato note uses same pos as previous..
                posb = lastposb; // .. same goes for posb.
            }
            else {
                // Legato mode is valid, but this is only a first note.
                for(i = 0; i < POLIPHONY; ++i)
                    if((partnote[i].status == KEY_PLAYING)
                       || (partnote[i].status == KEY_RELASED_AND_SUSTAINED))
                        RelaseNotePos(i);

                // Set posb
                posb = (pos + 1) % POLIPHONY; //We really want it (if the following fails)
                for(i = 0; i < POLIPHONY; ++i)
                    if((partnote[i].status == KEY_OFF) && (pos != i)) {
                        posb = i;
                        break;
                    }
            }
            lastposb = posb; // Keep a trace of used posb
        }
    }
    else     // Legato mode is either off or non-applicable.
    if(Ppolymode == 0) {   //if the mode is 'mono' turn off all other notes
        for(i = 0; i < POLIPHONY; ++i)
            if(partnote[i].status == KEY_PLAYING)
                RelaseNotePos(i);
        RelaseSustainedKeys();
    }
    lastlegatomodevalid = legatomodevalid;

    if(pos == -1)
        //test
        fprintf(stderr,
                "%s",
                "NOTES TOO MANY (> POLIPHONY) - (Part.cpp::NoteOn(..))\n");
    else {
        //start the note
        partnote[pos].status = KEY_PLAYING;
        partnote[pos].note   = note;
        if(legatomodevalid) {
            partnote[posb].status = KEY_PLAYING;
            partnote[posb].note   = note;
        }

        //this computes the velocity sensing of the part
        float vel = VelF(velocity / 127.0f, Pvelsns);

        //compute the velocity offset
        vel += (Pveloffs - 64.0f) / 64.0f;
        if(vel < 0.0f)
            vel = 0.0f;
        else
        if(vel > 1.0f)
            vel = 1.0f;

        //compute the keyshift
        int partkeyshift = (int)Pkeyshift - 64;
        int keyshift     = masterkeyshift + partkeyshift;

        //initialise note frequency
        float notebasefreq;
        if(Pdrummode == 0) {
            notebasefreq = microtonal->getnotefreq(note, keyshift);
            if(notebasefreq < 0.0f)
                return;                  //the key is no mapped
        }
        else
            notebasefreq = 440.0f * powf(2.0f, (note - 69.0f) / 12.0f);
        ;

        //Portamento
        if(oldfreq < 1.0f)
            oldfreq = notebasefreq;           //this is only the first note is played

        // For Mono/Legato: Force Portamento Off on first
        // notes. That means it is required that the previous note is
        // still held down or sustained for the Portamento to activate
        // (that's like Legato).
        int portamento = 0;
        if((Ppolymode != 0) || (not ismonofirstnote))
            // I added a third argument to the
            // ctl.initportamento(...) function to be able
            // to tell it if we're doing a legato note.
            portamento = ctl.initportamento(oldfreq, notebasefreq, doinglegato);

        if(portamento != 0)
            ctl.portamento.noteusing = pos;
        oldfreq = notebasefreq;

        lastpos = pos; // Keep a trace of used pos.

        if(doinglegato) {
            // Do Legato note
            if(Pkitmode == 0) { // "normal mode" legato note
                if((kit[0].Padenabled != 0)
                   && (partnote[pos].kititem[0].adnote != NULL)
                   && (partnote[posb].kititem[0].adnote != NULL)) {
                    partnote[pos].kititem[0].adnote->legatonote(notebasefreq,
                                                                vel,
                                                                portamento,
                                                                note,
                                                                true); //'true' is to tell it it's being called from here.
                    partnote[posb].kititem[0].adnote->legatonote(notebasefreq,
                                                                 vel,
                                                                 portamento,
                                                                 note,
                                                                 true);
                }

                if((kit[0].Psubenabled != 0)
                   && (partnote[pos].kititem[0].subnote != NULL)
                   && (partnote[posb].kititem[0].subnote != NULL)) {
                    partnote[pos].kititem[0].subnote->legatonote(
                        notebasefreq, vel, portamento, note, true);
                    partnote[posb].kititem[0].subnote->legatonote(
                        notebasefreq, vel, portamento, note, true);
                }

                if((kit[0].Ppadenabled != 0)
                   && (partnote[pos].kititem[0].padnote != NULL)
                   && (partnote[posb].kititem[0].padnote != NULL)) {
                    partnote[pos].kititem[0].padnote->legatonote(
                        notebasefreq, vel, portamento, note, true);
                    partnote[posb].kititem[0].padnote->legatonote(
                        notebasefreq, vel, portamento, note, true);
                }
            }
            else {   // "kit mode" legato note
                int ci = 0;
                for(int item = 0; item < NUM_KIT_ITEMS; ++item) {
                    if(kit[item].Pmuted != 0)
                        continue;
                    if((note < kit[item].Pminkey) || (note > kit[item].Pmaxkey))
                        continue;

                    if((lastnotecopy < kit[item].Pminkey)
                       || (lastnotecopy > kit[item].Pmaxkey))
                        continue;  // We will not perform legato across 2 key regions.

                    partnote[pos].kititem[ci].sendtoparteffect =
                        (kit[item].Psendtoparteffect <
                         NUM_PART_EFX ? kit[item].Psendtoparteffect :
                         NUM_PART_EFX);                                        //if this parameter is 127 for "unprocessed"
                    partnote[posb].kititem[ci].sendtoparteffect =
                        (kit[item].Psendtoparteffect <
                         NUM_PART_EFX ? kit[item].Psendtoparteffect :
                         NUM_PART_EFX);

                    if((kit[item].Padenabled != 0) && (kit[item].adpars != NULL)
                       && (partnote[pos].kititem[ci].adnote != NULL)
                       && (partnote[posb].kititem[ci].adnote != NULL)) {
                        partnote[pos].kititem[ci].adnote->legatonote(
                            notebasefreq, vel, portamento, note, true);
                        partnote[posb].kititem[ci].adnote->legatonote(
                            notebasefreq, vel, portamento, note, true);
                    }
                    if((kit[item].Psubenabled != 0)
                       && (kit[item].subpars != NULL)
                       && (partnote[pos].kititem[ci].subnote != NULL)
                       && (partnote[posb].kititem[ci].subnote != NULL)) {
                        partnote[pos].kititem[ci].subnote->legatonote(
                            notebasefreq, vel, portamento, note, true);
                        partnote[posb].kititem[ci].subnote->legatonote(
                            notebasefreq, vel, portamento, note, true);
                    }
                    if((kit[item].Ppadenabled != 0)
                       && (kit[item].padpars != NULL)
                       && (partnote[pos].kititem[ci].padnote != NULL)
                       && (partnote[posb].kititem[ci].padnote != NULL)) {
                        partnote[pos].kititem[ci].padnote->legatonote(
                            notebasefreq, vel, portamento, note, true);
                        partnote[posb].kititem[ci].padnote->legatonote(
                            notebasefreq, vel, portamento, note, true);
                    }

                    if((kit[item].adpars != NULL)
                       || (kit[item].subpars != NULL)
                       || (kit[item].padpars != NULL)) {
                        ci++;
                        if(((kit[item].Padenabled != 0)
                            || (kit[item].Psubenabled != 0)
                            || (kit[item].Ppadenabled != 0)) && (Pkitmode == 2))
                            break;
                    }
                }
                if(ci == 0) {
                    // No legato were performed at all, so pretend nothing happened:
                    monomemnotes.pop_back(); // Remove last note from the list.
                    lastnote = lastnotecopy; // Set lastnote back to previous value.
                }
            }
            return; // Ok, Legato note done, return.
        }

        partnote[pos].itemsplaying = 0;
        if(legatomodevalid)
            partnote[posb].itemsplaying = 0;

        if(Pkitmode == 0) { //init the notes for the "normal mode"
            partnote[pos].kititem[0].sendtoparteffect = 0;
            if(kit[0].Padenabled != 0)
                partnote[pos].kititem[0].adnote = new ADnote(kit[0].adpars,
                                                             &ctl,
                                                             notebasefreq,
                                                             vel,
                                                             portamento,
                                                             note,
                                                             false);
            if(kit[0].Psubenabled != 0)
                partnote[pos].kititem[0].subnote = new SUBnote(kit[0].subpars,
                                                               &ctl,
                                                               notebasefreq,
                                                               vel,
                                                               portamento,
                                                               note,
                                                               false);
            if(kit[0].Ppadenabled != 0)
                partnote[pos].kititem[0].padnote = new PADnote(kit[0].padpars,
                                                               &ctl,
                                                               notebasefreq,
                                                               vel,
                                                               portamento,
                                                               note,
                                                               false);
            if((kit[0].Padenabled != 0) || (kit[0].Psubenabled != 0)
               || (kit[0].Ppadenabled != 0))
                partnote[pos].itemsplaying++;

            // Spawn another note (but silent) if legatomodevalid==true
            if(legatomodevalid) {
                partnote[posb].kititem[0].sendtoparteffect = 0;
                if(kit[0].Padenabled != 0)
                    partnote[posb].kititem[0].adnote = new ADnote(kit[0].adpars,
                                                                  &ctl,
                                                                  notebasefreq,
                                                                  vel,
                                                                  portamento,
                                                                  note,
                                                                  true);     //true for silent.
                if(kit[0].Psubenabled != 0)
                    partnote[posb].kititem[0].subnote = new SUBnote(
                        kit[0].subpars,
                        &ctl,
                        notebasefreq,
                        vel,
                        portamento,
                        note,
                        true);
                if(kit[0].Ppadenabled != 0)
                    partnote[posb].kititem[0].padnote = new PADnote(
                        kit[0].padpars,
                        &ctl,
                        notebasefreq,
                        vel,
                        portamento,
                        note,
                        true);
                if((kit[0].Padenabled != 0) || (kit[0].Psubenabled != 0)
                   || (kit[0].Ppadenabled != 0))
                    partnote[posb].itemsplaying++;
            }
        }
        else    //init the notes for the "kit mode"
            for(int item = 0; item < NUM_KIT_ITEMS; ++item) {
                if(kit[item].Pmuted != 0)
                    continue;
                if((note < kit[item].Pminkey) || (note > kit[item].Pmaxkey))
                    continue;

                int ci = partnote[pos].itemsplaying; //ci=current item

                //if this parameter is 127 for "unprocessed"
                partnote[pos].kititem[ci].sendtoparteffect =
                    (kit[item].Psendtoparteffect < NUM_PART_EFX ?
                     kit[item].Psendtoparteffect : NUM_PART_EFX);

                if((kit[item].adpars != NULL) && ((kit[item].Padenabled) != 0))
                    partnote[pos].kititem[ci].adnote = new ADnote(
                        kit[item].adpars,
                        &ctl,
                        notebasefreq,
                        vel,
                        portamento,
                        note,
                        false);

                if((kit[item].subpars != NULL) && ((kit[item].Psubenabled) != 0))
                    partnote[pos].kititem[ci].subnote = new SUBnote(
                        kit[item].subpars,
                        &ctl,
                        notebasefreq,
                        vel,
                        portamento,
                        note,
                        false);

                if((kit[item].padpars != NULL) && ((kit[item].Ppadenabled) != 0))
                    partnote[pos].kititem[ci].padnote = new PADnote(
                        kit[item].padpars,
                        &ctl,
                        notebasefreq,
                        vel,
                        portamento,
                        note,
                        false);

                // Spawn another note (but silent) if legatomodevalid==true
                if(legatomodevalid) {
                    partnote[posb].kititem[ci].sendtoparteffect =
                        (kit[item].Psendtoparteffect <
                         NUM_PART_EFX ? kit[item].Psendtoparteffect :
                         NUM_PART_EFX);                                                                                                                 //if this parameter is 127 for "unprocessed"

                    if((kit[item].adpars != NULL)
                       && ((kit[item].Padenabled) != 0))
                        partnote[posb].kititem[ci].adnote = new ADnote(
                            kit[item].adpars,
                            &ctl,
                            notebasefreq,
                            vel,
                            portamento,
                            note,
                            true);                                            //true for silent.
                    if((kit[item].subpars != NULL)
                       && ((kit[item].Psubenabled) != 0))
                        partnote[posb].kititem[ci].subnote =
                            new SUBnote(kit[item].subpars,
                                        &ctl,
                                        notebasefreq,
                                        vel,
                                        portamento,
                                        note,
                                        true);
                    if((kit[item].padpars != NULL)
                       && ((kit[item].Ppadenabled) != 0))
                        partnote[posb].kititem[ci].padnote =
                            new PADnote(kit[item].padpars,
                                        &ctl,
                                        notebasefreq,
                                        vel,
                                        portamento,
                                        note,
                                        true);

                    if((kit[item].adpars != NULL) || (kit[item].subpars != NULL))
                        partnote[posb].itemsplaying++;
                }

                if((kit[item].adpars != NULL) || (kit[item].subpars != NULL)) {
                    partnote[pos].itemsplaying++;
                    if(((kit[item].Padenabled != 0)
                        || (kit[item].Psubenabled != 0)
                        || (kit[item].Ppadenabled != 0))
                       && (Pkitmode == 2))
                        break;
                }
            }
    }

    //this only relase the keys if there is maximum number of keys allowed
    setkeylimit(Pkeylimit);
}

/*
 * Note Off Messages
 */
void Part::NoteOff(unsigned char note) //relase the key
{
    int i;

    // This note is released, so we remove it from the list.
    if(not monomemnotes.empty())
        monomemnotes.remove(note);

    for(i = POLIPHONY - 1; i >= 0; i--) //first note in, is first out if there are same note multiple times
        if((partnote[i].status == KEY_PLAYING) && (partnote[i].note == note)) {
            if(ctl.sustain.sustain == 0) { //the sustain pedal is not pushed
                if((Ppolymode == 0) && (not monomemnotes.empty()))
                    MonoMemRenote();  // To play most recent still held note.
                else
                    RelaseNotePos(i);
                /// break;
            }
            else    //the sustain pedal is pushed
                partnote[i].status = KEY_RELASED_AND_SUSTAINED;
        }
}

void Part::PolyphonicAftertouch(unsigned char note,
                                unsigned char velocity,
                                int masterkeyshift)
{
    (void) masterkeyshift;
    if(!Pnoteon || (note < Pminkey) || (note > Pmaxkey))
        return;
    if(Pdrummode)
        return;

    // MonoMem stuff:
    if(!Ppolymode)   // if Poly is off

        monomem[note].velocity = velocity;       // Store this note's velocity.


    for(int i = 0; i < POLIPHONY; ++i)
        if((partnote[i].note == note) && (partnote[i].status == KEY_PLAYING)) {
            /* update velocity */
            // compute the velocity offset
            float vel =
                VelF(velocity / 127.0f, Pvelsns) + (Pveloffs - 64.0f) / 64.0f;
            vel = (vel < 0.0f) ? 0.0f : vel;
            vel = (vel > 1.0f) ? 1.0f : vel;

            if(!Pkitmode) { // "normal mode"
                if(kit[0].Padenabled)
                    partnote[i].kititem[0].adnote->setVelocity(vel);
                if(kit[0].Psubenabled)
                    partnote[i].kititem[0].subnote->setVelocity(vel);
                if(kit[0].Ppadenabled)
                    partnote[i].kititem[0].padnote->setVelocity(vel);
            }
            else     // "kit mode"
                for(int item = 0; item < NUM_KIT_ITEMS; ++item) {
                    if(kit[item].Pmuted)
                        continue;
                    if((note < kit[item].Pminkey)
                       || (note > kit[item].Pmaxkey))
                        continue;

                    if(kit[item].Padenabled)
                        partnote[i].kititem[item].adnote->setVelocity(vel);
                    if(kit[item].Psubenabled)
                        partnote[i].kititem[item].subnote->setVelocity(vel);
                    if(kit[item].Ppadenabled)
                        partnote[i].kititem[item].padnote->setVelocity(vel);
                }
        }

}

/*
 * Controllers
 */
void Part::SetController(unsigned int type, int par)
{
    switch(type) {
        case C_pitchwheel:
            ctl.setpitchwheel(par);
            break;
        case C_expression:
            ctl.setexpression(par);
            setPvolume(Pvolume); //update the volume
            break;
        case C_portamento:
            ctl.setportamento(par);
            break;
        case C_panning:
            ctl.setpanning(par);
            setPpanning(Ppanning); //update the panning
            break;
        case C_filtercutoff:
            ctl.setfiltercutoff(par);
            break;
        case C_filterq:
            ctl.setfilterq(par);
            break;
        case C_bandwidth:
            ctl.setbandwidth(par);
            break;
        case C_modwheel:
            ctl.setmodwheel(par);
            break;
        case C_fmamp:
            ctl.setfmamp(par);
            break;
        case C_volume:
            ctl.setvolume(par);
            if(ctl.volume.receive != 0)
                volume = ctl.volume.volume;
            else
                setPvolume(Pvolume);
            break;
        case C_sustain:
            ctl.setsustain(par);
            if(ctl.sustain.sustain == 0)
                RelaseSustainedKeys();
            break;
        case C_allsoundsoff:
            AllNotesOff(); //Panic
            break;
        case C_resetallcontrollers:
            ctl.resetall();
            RelaseSustainedKeys();
            if(ctl.volume.receive != 0)
                volume = ctl.volume.volume;
            else
                setPvolume(Pvolume);
            setPvolume(Pvolume); //update the volume
            setPpanning(Ppanning); //update the panning

            for(int item = 0; item < NUM_KIT_ITEMS; ++item) {
                if(kit[item].adpars == NULL)
                    continue;
                kit[item].adpars->GlobalPar.Reson->
                sendcontroller(C_resonance_center, 1.0f);

                kit[item].adpars->GlobalPar.Reson->
                sendcontroller(C_resonance_bandwidth, 1.0f);
            }
            //more update to add here if I add controllers
            break;
        case C_allnotesoff:
            RelaseAllKeys();
            break;
        case C_resonance_center:
            ctl.setresonancecenter(par);
            for(int item = 0; item < NUM_KIT_ITEMS; ++item) {
                if(kit[item].adpars == NULL)
                    continue;
                kit[item].adpars->GlobalPar.Reson->
                sendcontroller(C_resonance_center,
                               ctl.resonancecenter.relcenter);
            }
            break;
        case C_resonance_bandwidth:
            ctl.setresonancebw(par);
            kit[0].adpars->GlobalPar.Reson->
            sendcontroller(C_resonance_bandwidth, ctl.resonancebandwidth.relbw);
            break;
    }
}
/*
 * Relase the sustained keys
 */

void Part::RelaseSustainedKeys()
{
    // Let's call MonoMemRenote() on some conditions:
    if((Ppolymode == 0) && (not monomemnotes.empty()))
        if(monomemnotes.back() != lastnote) // Sustain controller manipulation would cause repeated same note respawn without this check.
            MonoMemRenote();  // To play most recent still held note.

    for(int i = 0; i < POLIPHONY; ++i)
        if(partnote[i].status == KEY_RELASED_AND_SUSTAINED)
            RelaseNotePos(i);
}

/*
 * Relase all keys
 */

void Part::RelaseAllKeys()
{
    for(int i = 0; i < POLIPHONY; ++i)
        if((partnote[i].status != KEY_RELASED)
           && (partnote[i].status != KEY_OFF)) //thanks to Frank Neumann
            RelaseNotePos(i);
}

// Call NoteOn(...) with the most recent still held key as new note
// (Made for Mono/Legato).
void Part::MonoMemRenote()
{
    unsigned char mmrtempnote = monomemnotes.back(); // Last list element.
    monomemnotes.pop_back(); // We remove it, will be added again in NoteOn(...).
    if(Pnoteon == 0)
        RelaseNotePos(lastpos);
    else
        NoteOn(mmrtempnote, monomem[mmrtempnote].velocity,
               monomem[mmrtempnote].mkeyshift);
}

/*
 * Release note at position
 */
void Part::RelaseNotePos(int pos)
{
    for(int j = 0; j < NUM_KIT_ITEMS; ++j) {
        if(partnote[pos].kititem[j].adnote != NULL)
            if(partnote[pos].kititem[j].adnote)
                partnote[pos].kititem[j].adnote->relasekey();

        if(partnote[pos].kititem[j].subnote != NULL)
            if(partnote[pos].kititem[j].subnote != NULL)
                partnote[pos].kititem[j].subnote->relasekey();

        if(partnote[pos].kititem[j].padnote != NULL)
            if(partnote[pos].kititem[j].padnote)
                partnote[pos].kititem[j].padnote->relasekey();
    }
    partnote[pos].status = KEY_RELASED;
}


/*
 * Kill note at position
 */
void Part::KillNotePos(int pos)
{
    partnote[pos].status = KEY_OFF;
    partnote[pos].note   = -1;
    partnote[pos].time   = 0;
    partnote[pos].itemsplaying = 0;

    for(int j = 0; j < NUM_KIT_ITEMS; ++j) {
        if(partnote[pos].kititem[j].adnote != NULL) {
            delete (partnote[pos].kititem[j].adnote);
            partnote[pos].kititem[j].adnote = NULL;
        }
        if(partnote[pos].kititem[j].subnote != NULL) {
            delete (partnote[pos].kititem[j].subnote);
            partnote[pos].kititem[j].subnote = NULL;
        }
        if(partnote[pos].kititem[j].padnote != NULL) {
            delete (partnote[pos].kititem[j].padnote);
            partnote[pos].kititem[j].padnote = NULL;
        }
    }
    if(pos == ctl.portamento.noteusing) {
        ctl.portamento.noteusing = -1;
        ctl.portamento.used      = 0;
    }
}


/*
 * Set Part's key limit
 */
void Part::setkeylimit(unsigned char Pkeylimit)
{
    this->Pkeylimit = Pkeylimit;
    int keylimit = Pkeylimit;
    if(keylimit == 0)
        keylimit = POLIPHONY - 5;

    //release old keys if the number of notes>keylimit
    if(Ppolymode != 0) {
        int notecount = 0;
        for(int i = 0; i < POLIPHONY; ++i)
            if((partnote[i].status == KEY_PLAYING)
               || (partnote[i].status == KEY_RELASED_AND_SUSTAINED))
                notecount++;

        int oldestnotepos = -1;
        if(notecount > keylimit)   //find out the oldest note
            for(int i = 0; i < POLIPHONY; ++i) {
                int maxtime = 0;
                if(((partnote[i].status == KEY_PLAYING)
                    || (partnote[i].status == KEY_RELASED_AND_SUSTAINED))
                   && (partnote[i].time > maxtime)) {
                    maxtime = partnote[i].time;
                    oldestnotepos = i;
                }
            }
        if(oldestnotepos != -1)
            RelaseNotePos(oldestnotepos);
    }
}


/*
 * Prepare all notes to be turned off
 */
void Part::AllNotesOff()
{
    killallnotes = 1;
}

void Part::RunNote(unsigned int k)
{
    unsigned noteplay = 0;
    for(int item = 0; item < partnote[k].itemsplaying; ++item) {
        int sendcurrenttofx = partnote[k].kititem[item].sendtoparteffect;

        for(unsigned type = 0; type < 3; ++type) {
            //Select a note
            SynthNote **note;
            if(type == 0)
                note = &partnote[k].kititem[item].adnote;
            else
            if(type == 1)
                note = &partnote[k].kititem[item].subnote;
            else
            if(type == 2)
                note = &partnote[k].kititem[item].padnote;

            //Process if it exists
            if(!(*note))
                continue;
            noteplay++;

            float *tmpoutr = getTmpBuffer();
            float *tmpoutl = getTmpBuffer();
            (*note)->noteout(&tmpoutl[0], &tmpoutr[0]);

            if((*note)->finished()) {
                delete (*note);
                (*note) = NULL;
            }
            for(int i = 0; i < synth->buffersize; ++i) { //add the note to part(mix)
                partfxinputl[sendcurrenttofx][i] += tmpoutl[i];
                partfxinputr[sendcurrenttofx][i] += tmpoutr[i];
            }
            returnTmpBuffer(tmpoutr);
            returnTmpBuffer(tmpoutl);
        }
    }

    //Kill note if there is no synth on that note
    if(noteplay == 0)
        KillNotePos(k);
}

/*
 * Compute Part samples and store them in the partoutl[] and partoutr[]
 */
void Part::ComputePartSmps()
{
    for(unsigned nefx = 0; nefx < NUM_PART_EFX + 1; ++nefx)
        for(int i = 0; i < synth->buffersize; ++i) {
            partfxinputl[nefx][i] = 0.0f;
            partfxinputr[nefx][i] = 0.0f;
        }

    for(unsigned k = 0; k < POLIPHONY; ++k) {
        if(partnote[k].status == KEY_OFF)
            continue;
        partnote[k].time++;
        //get the sampledata of the note and kill it if it's finished
        RunNote(k);
    }


    //Apply part's effects and mix them
    for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) {
        if(!Pefxbypass[nefx]) {
            partefx[nefx]->out(partfxinputl[nefx], partfxinputr[nefx]);
            if(Pefxroute[nefx] == 2)
                for(int i = 0; i < synth->buffersize; ++i) {
                    partfxinputl[nefx + 1][i] += partefx[nefx]->efxoutl[i];
                    partfxinputr[nefx + 1][i] += partefx[nefx]->efxoutr[i];
                }
        }
        int routeto = ((Pefxroute[nefx] == 0) ? nefx + 1 : NUM_PART_EFX);
        for(int i = 0; i < synth->buffersize; ++i) {
            partfxinputl[routeto][i] += partfxinputl[nefx][i];
            partfxinputr[routeto][i] += partfxinputr[nefx][i];
        }
    }
    for(int i = 0; i < synth->buffersize; ++i) {
        partoutl[i] = partfxinputl[NUM_PART_EFX][i];
        partoutr[i] = partfxinputr[NUM_PART_EFX][i];
    }

    //Kill All Notes if killallnotes!=0
    if(killallnotes != 0) {
        for(int i = 0; i < synth->buffersize; ++i) {
            float tmp = (synth->buffersize_f - i) / synth->buffersize_f;
            partoutl[i] *= tmp;
            partoutr[i] *= tmp;
        }
        for(int k = 0; k < POLIPHONY; ++k)
            KillNotePos(k);
        killallnotes = 0;
        for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx)
            partefx[nefx]->cleanup();
    }
    ctl.updateportamento();
}

/*
 * Parameter control
 */
void Part::setPvolume(char Pvolume_)
{
    Pvolume = Pvolume_;
    volume  =
        dB2rap((Pvolume - 96.0f) / 96.0f * 40.0f) * ctl.expression.relvolume;
}

void Part::setPpanning(char Ppanning_)
{
    Ppanning = Ppanning_;
    panning  = Ppanning / 127.0f + ctl.panning.pan;
    if(panning < 0.0f)
        panning = 0.0f;
    else
    if(panning > 1.0f)
        panning = 1.0f;
}

/*
 * Enable or disable a kit item
 */
void Part::setkititemstatus(int kititem, int Penabled_)
{
    if((kititem == 0) || (kititem >= NUM_KIT_ITEMS))
        return;                                        //nonexistent kit item and the first kit item is always enabled
    kit[kititem].Penabled = Penabled_;

    bool resetallnotes = false;
    if(Penabled_ == 0) {
        if(kit[kititem].adpars != NULL)
            delete (kit[kititem].adpars);
        if(kit[kititem].subpars != NULL)
            delete (kit[kititem].subpars);
        if(kit[kititem].padpars != NULL) {
            delete (kit[kititem].padpars);
            resetallnotes = true;
        }
        kit[kititem].adpars   = NULL;
        kit[kititem].subpars  = NULL;
        kit[kititem].padpars  = NULL;
        kit[kititem].Pname[0] = '\0';
    }
    else {
        if(kit[kititem].adpars == NULL)
            kit[kititem].adpars = new ADnoteParameters(fft);
        if(kit[kititem].subpars == NULL)
            kit[kititem].subpars = new SUBnoteParameters();
        if(kit[kititem].padpars == NULL)
            kit[kititem].padpars = new PADnoteParameters(fft, mutex);
    }

    if(resetallnotes)
        for(int k = 0; k < POLIPHONY; ++k)
            KillNotePos(k);
}

void Part::add2XMLinstrument(XMLwrapper *xml)
{
    xml->beginbranch("INFO");
    xml->addparstr("name", (char *)Pname);
    xml->addparstr("author", (char *)info.Pauthor);
    xml->addparstr("comments", (char *)info.Pcomments);
    xml->addpar("type", info.Ptype);
    xml->endbranch();


    xml->beginbranch("INSTRUMENT_KIT");
    xml->addpar("kit_mode", Pkitmode);
    xml->addparbool("drum_mode", Pdrummode);

    for(int i = 0; i < NUM_KIT_ITEMS; ++i) {
        xml->beginbranch("INSTRUMENT_KIT_ITEM", i);
        xml->addparbool("enabled", kit[i].Penabled);
        if(kit[i].Penabled != 0) {
            xml->addparstr("name", (char *)kit[i].Pname);

            xml->addparbool("muted", kit[i].Pmuted);
            xml->addpar("min_key", kit[i].Pminkey);
            xml->addpar("max_key", kit[i].Pmaxkey);

            xml->addpar("send_to_instrument_effect", kit[i].Psendtoparteffect);

            xml->addparbool("add_enabled", kit[i].Padenabled);
            if((kit[i].Padenabled != 0) && (kit[i].adpars != NULL)) {
                xml->beginbranch("ADD_SYNTH_PARAMETERS");
                kit[i].adpars->add2XML(xml);
                xml->endbranch();
            }

            xml->addparbool("sub_enabled", kit[i].Psubenabled);
            if((kit[i].Psubenabled != 0) && (kit[i].subpars != NULL)) {
                xml->beginbranch("SUB_SYNTH_PARAMETERS");
                kit[i].subpars->add2XML(xml);
                xml->endbranch();
            }

            xml->addparbool("pad_enabled", kit[i].Ppadenabled);
            if((kit[i].Ppadenabled != 0) && (kit[i].padpars != NULL)) {
                xml->beginbranch("PAD_SYNTH_PARAMETERS");
                kit[i].padpars->add2XML(xml);
                xml->endbranch();
            }
        }
        xml->endbranch();
    }
    xml->endbranch();

    xml->beginbranch("INSTRUMENT_EFFECTS");
    for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) {
        xml->beginbranch("INSTRUMENT_EFFECT", nefx);
        xml->beginbranch("EFFECT");
        partefx[nefx]->add2XML(xml);
        xml->endbranch();

        xml->addpar("route", Pefxroute[nefx]);
        partefx[nefx]->setdryonly(Pefxroute[nefx] == 2);
        xml->addparbool("bypass", Pefxbypass[nefx]);
        xml->endbranch();
    }
    xml->endbranch();
}

void Part::add2XML(XMLwrapper *xml)
{
    //parameters
    xml->addparbool("enabled", Penabled);
    if((Penabled == 0) && (xml->minimal))
        return;

    xml->addpar("volume", Pvolume);
    xml->addpar("panning", Ppanning);

    xml->addpar("min_key", Pminkey);
    xml->addpar("max_key", Pmaxkey);
    xml->addpar("key_shift", Pkeyshift);
    xml->addpar("rcv_chn", Prcvchn);

    xml->addpar("velocity_sensing", Pvelsns);
    xml->addpar("velocity_offset", Pveloffs);

    xml->addparbool("note_on", Pnoteon);
    xml->addparbool("poly_mode", Ppolymode);
    xml->addpar("legato_mode", Plegatomode);
    xml->addpar("key_limit", Pkeylimit);

    xml->beginbranch("INSTRUMENT");
    add2XMLinstrument(xml);
    xml->endbranch();

    xml->beginbranch("CONTROLLER");
    ctl.add2XML(xml);
    xml->endbranch();
}

int Part::saveXML(const char *filename)
{
    XMLwrapper *xml;
    xml = new XMLwrapper();

    xml->beginbranch("INSTRUMENT");
    add2XMLinstrument(xml);
    xml->endbranch();

    int result = xml->saveXMLfile(filename);
    delete (xml);
    return result;
}

int Part::loadXMLinstrument(const char *filename) /*{*/
{
    XMLwrapper *xml = new XMLwrapper();
    if(xml->loadXMLfile(filename) < 0) {
        delete (xml);
        return -1;
    }

    if(xml->enterbranch("INSTRUMENT") == 0)
        return -10;
    getfromXMLinstrument(xml);
    xml->exitbranch();

    delete (xml);
    return 0;
} /*}*/

void Part::applyparameters(bool lockmutex) /*{*/
{
    for(int n = 0; n < NUM_KIT_ITEMS; ++n)
        if((kit[n].padpars != NULL) && (kit[n].Ppadenabled != 0))
            kit[n].padpars->applyparameters(lockmutex);
} /*}*/

void Part::getfromXMLinstrument(XMLwrapper *xml)
{
    if(xml->enterbranch("INFO")) {
        xml->getparstr("name", (char *)Pname, PART_MAX_NAME_LEN);
        xml->getparstr("author", (char *)info.Pauthor, MAX_INFO_TEXT_SIZE);
        xml->getparstr("comments", (char *)info.Pcomments, MAX_INFO_TEXT_SIZE);
        info.Ptype = xml->getpar("type", info.Ptype, 0, 16);

        xml->exitbranch();
    }

    if(xml->enterbranch("INSTRUMENT_KIT")) {
        Pkitmode  = xml->getpar127("kit_mode", Pkitmode);
        Pdrummode = xml->getparbool("drum_mode", Pdrummode);

        setkititemstatus(0, 0);
        for(int i = 0; i < NUM_KIT_ITEMS; ++i) {
            if(xml->enterbranch("INSTRUMENT_KIT_ITEM", i) == 0)
                continue;
            setkititemstatus(i, xml->getparbool("enabled", kit[i].Penabled));
            if(kit[i].Penabled == 0) {
                xml->exitbranch();
                continue;
            }

            xml->getparstr("name", (char *)kit[i].Pname, PART_MAX_NAME_LEN);

            kit[i].Pmuted  = xml->getparbool("muted", kit[i].Pmuted);
            kit[i].Pminkey = xml->getpar127("min_key", kit[i].Pminkey);
            kit[i].Pmaxkey = xml->getpar127("max_key", kit[i].Pmaxkey);

            kit[i].Psendtoparteffect = xml->getpar127(
                "send_to_instrument_effect",
                kit[i].Psendtoparteffect);

            kit[i].Padenabled = xml->getparbool("add_enabled",
                                                kit[i].Padenabled);
            if(xml->enterbranch("ADD_SYNTH_PARAMETERS")) {
                kit[i].adpars->getfromXML(xml);
                xml->exitbranch();
            }

            kit[i].Psubenabled = xml->getparbool("sub_enabled",
                                                 kit[i].Psubenabled);
            if(xml->enterbranch("SUB_SYNTH_PARAMETERS")) {
                kit[i].subpars->getfromXML(xml);
                xml->exitbranch();
            }

            kit[i].Ppadenabled = xml->getparbool("pad_enabled",
                                                 kit[i].Ppadenabled);
            if(xml->enterbranch("PAD_SYNTH_PARAMETERS")) {
                kit[i].padpars->getfromXML(xml);
                xml->exitbranch();
            }

            xml->exitbranch();
        }

        xml->exitbranch();
    }


    if(xml->enterbranch("INSTRUMENT_EFFECTS")) {
        for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) {
            if(xml->enterbranch("INSTRUMENT_EFFECT", nefx) == 0)
                continue;
            if(xml->enterbranch("EFFECT")) {
                partefx[nefx]->getfromXML(xml);
                xml->exitbranch();
            }

            Pefxroute[nefx] = xml->getpar("route",
                                          Pefxroute[nefx],
                                          0,
                                          NUM_PART_EFX);
            partefx[nefx]->setdryonly(Pefxroute[nefx] == 2);
            Pefxbypass[nefx] = xml->getparbool("bypass", Pefxbypass[nefx]);
            xml->exitbranch();
        }
        xml->exitbranch();
    }
}

void Part::getfromXML(XMLwrapper *xml)
{
    Penabled = xml->getparbool("enabled", Penabled);

    setPvolume(xml->getpar127("volume", Pvolume));
    setPpanning(xml->getpar127("panning", Ppanning));

    Pminkey   = xml->getpar127("min_key", Pminkey);
    Pmaxkey   = xml->getpar127("max_key", Pmaxkey);
    Pkeyshift = xml->getpar127("key_shift", Pkeyshift);
    Prcvchn   = xml->getpar127("rcv_chn", Prcvchn);

    Pvelsns  = xml->getpar127("velocity_sensing", Pvelsns);
    Pveloffs = xml->getpar127("velocity_offset", Pveloffs);

    Pnoteon     = xml->getparbool("note_on", Pnoteon);
    Ppolymode   = xml->getparbool("poly_mode", Ppolymode);
    Plegatomode = xml->getparbool("legato_mode", Plegatomode); //older versions
    if(!Plegatomode)
        Plegatomode = xml->getpar127("legato_mode", Plegatomode);
    Pkeylimit = xml->getpar127("key_limit", Pkeylimit);


    if(xml->enterbranch("INSTRUMENT")) {
        getfromXMLinstrument(xml);
        xml->exitbranch();
    }

    if(xml->enterbranch("CONTROLLER")) {
        ctl.getfromXML(xml);
        xml->exitbranch();
    }
}
