/* 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 #include #include 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;ndefaults(); kit[0].subpars->defaults(); kit[0].padpars->defaults(); for (int nefx=0;nefxdefaults(); Pefxroute[nefx]=0;//route to next effect }; }; /* * Cleanup the part */ void Part::cleanup(){ for (int k=0;kcleanup(); for (int n=0;nPmaxkey)) return; pos=-1; for (i=0;i 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;itemkit[item].Pmaxkey)) continue; int ci=partnote[pos].itemsplaying;//ci=current item partnote[pos].kititem[ci].sendtoparteffect=( kit[item].Psendtoparteffect=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;itemGlobalPar.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;itemGlobalPar.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;irelasekey(); 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;jPkeylimit=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;ikeylimit){//find out the oldest note for (int i=0;imaxtime)){ 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;nefxready!=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;iready!=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;ifinished()!=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;iout(partfxinputl[nefx],partfxinputr[nefx]); if (Pefxroute[nefx]==2){ for (i=0;iefxoutl[i]; partfxinputr[nefx+1][i]+=partefx[nefx]->efxoutr[i]; }; }; }; int routeto=((Pefxroute[nefx]==0) ? nefx+1 : NUM_PART_EFX); for (i=0;icleanup(); }; }; 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;kbeginbranch("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;ibeginbranch("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;nefxbeginbranch("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;napplyparameters(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;ienterbranch("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;nefxenterbranch("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(); }; };