/*
  ZynAddSubFX - a software synthesizer
 
  Part.C - 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) 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 "Master.h"
#include "Part.h"
#include "Microtonal.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

Part::Part(Microtonal *microtonal_,FFTwrapper *fft_, Master* master_){
    microtonal=microtonal_;    
    fft=fft_;
    master=master_;
    partoutl=new REALTYPE [SOUND_BUFFER_SIZE];
    partoutr=new REALTYPE [SOUND_BUFFER_SIZE];
    tmpoutl=new REALTYPE [SOUND_BUFFER_SIZE];
    tmpoutr=new REALTYPE [SOUND_BUFFER_SIZE];
    
    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,master);
//    ADPartParameters=kit[0].adpars;
//    SUBPartParameters=kit[0].subpars;

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

    for (int n=0;n<NUM_PART_EFX+1;n++) {
	partfxinputl[n]=new REALTYPE [SOUND_BUFFER_SIZE];
	partfxinputr[n]=new REALTYPE [SOUND_BUFFER_SIZE];
	Pefxbypass[n]=false;
    };

    killallnotes=0;
    oldfreq=-1.0;

    int i,j;
    for (i=0;i<POLIPHONY;i++){
      partnote[i].status=KEY_OFF;
      partnote[i].note=-1;
      partnote[i].itemsplaying=0;
      for (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.5;
    lastnote=-1;
    
    
    defaults();
};

void Part::defaults(){
    Penabled=0;
    Pminkey=0;
    Pmaxkey=127;
    Pnoteon=1;
    Ppolymode=1;
    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(){
    for (int k=0;k<POLIPHONY;k++) KillNotePos(k);

    memcpy(partoutl, denormalkillbuf, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);
    memcpy(partoutr, denormalkillbuf, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);
    memset(tmpoutl, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);
    memset(tmpoutr, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);

    ctl.resetall();
    for (int nefx=0;nefx<NUM_PART_EFX;nefx++) partefx[nefx]->cleanup();
    for (int n=0;n<NUM_PART_EFX+1;n++) {
      memcpy(partfxinputl[n], denormalkillbuf, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);
      memcpy(partfxinputr[n], denormalkillbuf, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);
    };
};

Part::~Part(){
    cleanup();
    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);
    delete (tmpoutl);
    delete (tmpoutr);
    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;    
    
    lastnote=note;    
    if ((note<Pminkey)||(note>Pmaxkey)) return;
    
    pos=-1;
    for (i=0;i<POLIPHONY;i++){
        if (partnote[i].status==KEY_OFF){
    	    pos=i;
	    break;
	};
    };

    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) NoteOff(partnote[i].note);
	RelaseSustainedKeys();    
    };
    
    if (pos==-1){
        //test
	fprintf(stderr,"%s","NOTES TOO MANY (> POLIPHONY) - (Part.C::NoteOn(..))\n");
    } else {
	if (Pnoteon!=0){
	    //start the note
    	    partnote[pos].status=KEY_PLAYING;
    	    partnote[pos].note=note;

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

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

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

	    //initialise note frequency
	    REALTYPE notebasefreq;
	    if (Pdrummode==0){
		notebasefreq=microtonal->getnotefreq(note,keyshift);
		if (notebasefreq<0.0) return;//the key is no mapped
	    } else {
		notebasefreq=440.0*pow(2.0,(note-69.0)/12.0);
	    };
	    
	    //Portamento
	    if (oldfreq<1.0) oldfreq=notebasefreq;//this is only the first note is played
	    
	    int portamento=ctl.initportamento(oldfreq,notebasefreq);
	    
	    if (portamento!=0) ctl.portamento.noteusing=pos;
	    oldfreq=notebasefreq;
	    
	    partnote[pos].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);
        	if (kit[0].Psubenabled!=0) partnote[pos].kititem[0].subnote=new SUBnote(kit[0].subpars,&ctl,notebasefreq,vel,portamento,note);
        	if (kit[0].Ppadenabled!=0) partnote[pos].kititem[0].padnote=new PADnote(kit[0].padpars,&ctl,notebasefreq,vel,portamento,note);
		if ((kit[0].Padenabled!=0)||(kit[0].Psubenabled!=0)||(kit[0].Ppadenabled!=0)) partnote[pos].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

		    partnote[pos].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[pos].kititem[ci].adnote=new ADnote(kit[item].adpars,&ctl,notebasefreq,vel,portamento,note);

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

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

		    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;    
    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
		RelaseNotePos(i);
		break;
	    } else {//the sustain pedal is pushed
		partnote[i].status=KEY_RELASED_AND_SUSTAINED;
	    };
	};
    };
};

/*
 * 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.0);
			    
			    kit[item].adpars->GlobalPar.Reson->
			       sendcontroller(C_resonance_bandwidth,1.0);
			  };			     
			  //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(){
    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);
    };
};

/*
 * 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,maxtime=0;
	if (notecount>keylimit){//find out the oldest note
	    for (int i=0;i<POLIPHONY;i++){
		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;    
};


/*
 * Compute Part samples and store them in the partoutl[] and partoutr[]
 */
void Part::ComputePartSmps(){
    int i, k;
    int noteplay;//0 if there is nothing activated
    for (int nefx=0;nefx<NUM_PART_EFX+1;nefx++){
      memset(partfxinputl[nefx], 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);
      memset(partfxinputr[nefx], 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);
	};
    
    for (k=0;k<POLIPHONY;k++){
    	if (partnote[k].status==KEY_OFF) continue;
	noteplay=0;	
	partnote[k].time++;
	//get the sampledata of the note and kill it if it's finished

        for (int item=0;item<partnote[k].itemsplaying;item++){

	    int sendcurrenttofx=partnote[k].kititem[item].sendtoparteffect;
	    
	    ADnote *adnote=partnote[k].kititem[item].adnote;
	    SUBnote *subnote=partnote[k].kititem[item].subnote;
	    PADnote *padnote=partnote[k].kititem[item].padnote;
	   //get from the ADnote
            if (adnote!=NULL) {
    		noteplay++;
		if (adnote->ready!=0) adnote->noteout(&tmpoutl[0],&tmpoutr[0]);
            	    else {
                              memset(tmpoutl, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);
                              memset(tmpoutr, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);
                              }
		if (adnote->finished()!=0){
		    delete (adnote);
		    partnote[k].kititem[item].adnote=NULL;
		};
		for (i=0;i<SOUND_BUFFER_SIZE;i++){//add the ADnote to part(mix)
		    partfxinputl[sendcurrenttofx][i]+=tmpoutl[i];
		    partfxinputr[sendcurrenttofx][i]+=tmpoutr[i];
		};
	    };
	    //get from the SUBnote
    	    if (subnote!=NULL) {
    		noteplay++;
		if (subnote->ready!=0) subnote->noteout(&tmpoutl[0],&tmpoutr[0]);
            	    else {
                              memset(tmpoutl, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);
                              memset(tmpoutr, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);
                              }

	        for (i=0;i<SOUND_BUFFER_SIZE;i++){//add the SUBnote to part(mix)
		    partfxinputl[sendcurrenttofx][i]+=tmpoutl[i];
		    partfxinputr[sendcurrenttofx][i]+=tmpoutr[i];
		};
		if (subnote->finished()!=0){
		    delete (subnote);
		    partnote[k].kititem[item].subnote=NULL;
		};
	    };	
	   //get from the PADnote
            if (padnote!=NULL) {
    		noteplay++;
		if (padnote->ready!=0) padnote->noteout(&tmpoutl[0],&tmpoutr[0]);
            	    else {
                              memset(tmpoutl, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);
                              memset(tmpoutr, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE);
                              }
		if (padnote->finished()!=0){
		    delete (padnote);
		    partnote[k].kititem[item].padnote=NULL;
		};
		for (i=0;i<SOUND_BUFFER_SIZE;i++){//add the PADnote to part(mix)
		    partfxinputl[sendcurrenttofx][i]+=tmpoutl[i];
		    partfxinputr[sendcurrenttofx][i]+=tmpoutr[i];
		};
	    };

	};
	//Kill note if there is no synth on that note
	if (noteplay==0) KillNotePos(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 (i=0;i<SOUND_BUFFER_SIZE;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 (i=0;i<SOUND_BUFFER_SIZE;i++){
	    partfxinputl[routeto][i]+=partfxinputl[nefx][i];
	    partfxinputr[routeto][i]+=partfxinputr[nefx][i];
	};
	
    };
    for (i=0;i<SOUND_BUFFER_SIZE;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 (i=0;i<SOUND_BUFFER_SIZE;i++) {
	    REALTYPE tmp=(SOUND_BUFFER_SIZE-i)/(REALTYPE) SOUND_BUFFER_SIZE;
	    partoutl[i]*=tmp;
	    partoutr[i]*=tmp;
	    tmpoutl[i]=0.0;
	    tmpoutr[i]=0.0;
	};
	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.0)/96.0*40.0)*ctl.expression.relvolume;
};

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

};

/*
 * 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,master);
    };
    
    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("key_limit",Pkeylimit);

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

int Part::saveXML(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(){
    for (int n=0;n<NUM_KIT_ITEMS;n++){
	if ((kit[n].padpars!=NULL)&&(kit[n].Ppadenabled!=0)) kit[n].padpars->applyparameters(true);
    };
};

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);
    Pkeylimit=xml->getpar127("key_limit",Pkeylimit);


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

};