zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

commit d1513405b098b4223fe56f3d4d8885efda7f306c
parent 858c0930dacdaa46ff3488920384834c549e8db5
Author: paulnasca <paulnasca>
Date:   Tue, 14 Nov 2006 16:26:36 +0000

*** empty log message ***

Diffstat:
MChangeLog | 2++
Msrc/Misc/Part.C | 128++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/Misc/Part.h | 292++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/Synth/ADnote.C | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/Synth/ADnote.h | 20++++++++++++++++++--
Msrc/Synth/PADnote.C | 106++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/Synth/PADnote.h | 20++++++++++++++++++--
Msrc/Synth/SUBnote.C | 104++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/Synth/SUBnote.h | 19+++++++++++++++++--
Msrc/globals.h | 1+
10 files changed, 605 insertions(+), 196 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -814,6 +814,8 @@ 01 Nov 2006 - Adaugat patch-urile de Jack Midi si LASH de Lars Luthman 06 Nov 2006 - Aplicat un patch "Fix for ALSA system lockup" de Lars Luthman 10 Nov 2006 - Aplicat un patch "zyn-extendedmono_v4_update-061110.diff.gz" de Gerald Folcher +14 Nov 2006 - Aplicat un patch "zyn-CVS-extendedmono_v5_update-061113.diff.gz" de Gerald Folcher + diff --git a/src/Misc/Part.C b/src/Misc/Part.C @@ -78,6 +78,7 @@ Part::Part(Microtonal *microtonal_,FFTwrapper *fft_, pthread_mutex_t *mutex_){ oldvolumel=oldvolumer=0.5; lastnote=-1; lastpos=0; // lastpos will store previously used NoteOn(...)'s pos. + lastlegatomodevalid=false; // To store previous legatomodevalid value. defaults(); @@ -185,16 +186,18 @@ 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. + int lastnotecopy=lastnote;//Useful after lastnote has been changed. - if (Pnoteon==0) return; // (This check was performed later) + if (Pnoteon==0) return; if ((note<Pminkey)||(note>Pmaxkey)) return; // MonoMem stuff: - if (Ppolymode==0){ // If Poly is off (should we check for drummode too ?). + 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, @@ -219,21 +222,35 @@ void Part::NoteOn(unsigned char note,unsigned char velocity,int masterkeyshift){ }; if ((Plegatomode!=0) && (Pdrummode==0)){ - // Legato mode is on and applicable. if (Ppolymode!=0){ fprintf(stderr, "ZynAddSubFX WARNING: Poly and Legato modes are both On, that should not happen ! ... Disabling Legato mode ! - (Part.C::NoteOn(..))\n"); Plegatomode=0; } else { - if (not ismonofirstnote){ - // At least one other key is held or sustained + // 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. + 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 @@ -242,16 +259,20 @@ void Part::NoteOn(unsigned char note,unsigned char velocity,int masterkeyshift){ RelaseSustainedKeys(); } } + lastlegatomodevalid=legatomodevalid; 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; + if (legatomodevalid){ + partnote[posb].status=KEY_PLAYING; + partnote[posb].note=note; + } //this computes the velocity sensing of the part REALTYPE vel=VelF(velocity/127.0,Pvelsns); @@ -297,16 +318,25 @@ void Part::NoteOn(unsigned char note,unsigned char velocity,int masterkeyshift){ // Do Legato note if (Pkitmode==0){ // "normal mode" legato note if ((kit[0].Padenabled!=0) - && (partnote[pos].kititem[0].adnote!=NULL)) - partnote[pos].kititem[0].adnote->ADlegatonote(notebasefreq, vel, portamento, note); + && (partnote[pos].kititem[0].adnote!=NULL) + && (partnote[posb].kititem[0].adnote!=NULL)){ + partnote[pos].kititem[0].adnote->ADlegatonote(notebasefreq, vel, portamento, note, true);//'true' is to tell it it's being called from here. + partnote[posb].kititem[0].adnote->ADlegatonote(notebasefreq, vel, portamento, note, true); + } if ((kit[0].Psubenabled!=0) - && (partnote[pos].kititem[0].subnote!=NULL)) - partnote[pos].kititem[0].subnote->SUBlegatonote(notebasefreq, vel, portamento, note); + && (partnote[pos].kititem[0].subnote!=NULL) + && (partnote[posb].kititem[0].subnote!=NULL)){ + partnote[pos].kititem[0].subnote->SUBlegatonote(notebasefreq, vel, portamento, note, true); + partnote[posb].kititem[0].subnote->SUBlegatonote(notebasefreq, vel, portamento, note, true); + } if ((kit[0].Ppadenabled!=0) - && (partnote[pos].kititem[0].padnote!=NULL)) - partnote[pos].kititem[0].padnote->PADlegatonote(notebasefreq, vel, portamento, note); + && (partnote[pos].kititem[0].padnote!=NULL) + && (partnote[posb].kititem[0].padnote!=NULL)){ + partnote[pos].kititem[0].padnote->PADlegatonote(notebasefreq, vel, portamento, note, true); + partnote[posb].kititem[0].padnote->PADlegatonote(notebasefreq, vel, portamento, note, true); + } } else { // "kit mode" legato note int ci=0; @@ -318,23 +348,28 @@ void Part::NoteOn(unsigned char note,unsigned char velocity,int masterkeyshift){ ||(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[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[pos].kititem[ci].adnote->ADlegatonote(notebasefreq,vel,portamento,note); - + && (partnote[pos].kititem[ci].adnote!=NULL) + && (partnote[posb].kititem[ci].adnote!=NULL)){ + partnote[pos].kititem[ci].adnote->ADlegatonote(notebasefreq,vel,portamento,note,true); + partnote[posb].kititem[ci].adnote->ADlegatonote(notebasefreq,vel,portamento,note,true); + } if ((kit[item].Psubenabled!=0) && (kit[item].subpars!=NULL) - && (partnote[pos].kititem[ci].subnote!=NULL)) - partnote[pos].kititem[ci].subnote->SUBlegatonote(notebasefreq,vel,portamento,note); - + && (partnote[pos].kititem[ci].subnote!=NULL) + && (partnote[posb].kititem[ci].subnote!=NULL)){ + partnote[pos].kititem[ci].subnote->SUBlegatonote(notebasefreq,vel,portamento,note,true); + partnote[posb].kititem[ci].subnote->SUBlegatonote(notebasefreq,vel,portamento,note,true); + } if ((kit[item].Ppadenabled!=0) && (kit[item].padpars!=NULL) - && (partnote[pos].kititem[ci].padnote!=NULL)) - partnote[pos].kititem[ci].padnote->PADlegatonote(notebasefreq,vel,portamento,note); + && (partnote[pos].kititem[ci].padnote!=NULL) + && (partnote[posb].kititem[ci].padnote!=NULL)){ + partnote[pos].kititem[ci].padnote->PADlegatonote(notebasefreq,vel,portamento,note,true); + partnote[posb].kititem[ci].padnote->PADlegatonote(notebasefreq,vel,portamento,note,true); + } - /* In the non-legato equivalent, (kit[item].padpars!=NULL) - is not checked, is there a reason for that or is it a bug ? */ 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; @@ -346,17 +381,28 @@ void Part::NoteOn(unsigned char note,unsigned char velocity,int masterkeyshift){ lastnote=lastnotecopy; // Set lastnote back to previous value. } } - return; // Ok, Legato note stuff done, return. + 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); - 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) 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; @@ -368,13 +414,28 @@ void Part::NoteOn(unsigned char note,unsigned char velocity,int masterkeyshift){ 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); + 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); + 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); + 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++; @@ -383,7 +444,6 @@ void Part::NoteOn(unsigned char note,unsigned char velocity,int masterkeyshift){ }; }; }; - /// }; }; //this only relase the keys if there is maximum number of keys allowed diff --git a/src/Misc/Part.h b/src/Misc/Part.h @@ -41,152 +41,154 @@ #include <list> // For the monomemnotes list. class Part{ - public: - Part(Microtonal *microtonal_,FFTwrapper *fft_,pthread_mutex_t *mutex_); - ~Part(); - /* Midi commands implemented */ - void NoteOn(unsigned char note,unsigned char velocity,int masterkeyshift); - void NoteOff(unsigned char note); - void AllNotesOff();//panic - void SetController(unsigned int type,int par); - void RelaseSustainedKeys();//this is called when the sustain pedal is relased - void RelaseAllKeys();//this is called on AllNotesOff controller - - /* The synthesizer part output */ - void ComputePartSmps();//Part output - - //instrumentonly: 0 - save all, 1 - save only instrumnet, 2 - save only instrument without the name(used in bank) - - - //saves the instrument settings to a XML file - //returns 0 for ok or <0 if there is an error - int saveXML(char *filename); - int loadXMLinstrument(const char *filename); - - void add2XML(XMLwrapper *xml); - void add2XMLinstrument(XMLwrapper *xml); - - void defaults(); - void defaultsinstrument(); - - void applyparameters(); - - void getfromXML(XMLwrapper *xml); - void getfromXMLinstrument(XMLwrapper *xml); - - void cleanup(); - - // ADnoteParameters *ADPartParameters; - // SUBnoteParameters *SUBPartParameters; - - //the part's kit - struct { - unsigned char Penabled,Pmuted,Pminkey,Pmaxkey; - unsigned char *Pname; - unsigned char Padenabled,Psubenabled,Ppadenabled; - unsigned char Psendtoparteffect; - ADnoteParameters *adpars; - SUBnoteParameters *subpars; - PADnoteParameters *padpars; - } kit[NUM_KIT_ITEMS]; - - - //Part parameters - void setkeylimit(unsigned char Pkeylimit); - void setkititemstatus(int kititem,int Penabled_); - - unsigned char Penabled;//if the part is enabled - unsigned char Pvolume;//part volume - unsigned char Pminkey;//the minimum key that the part receives noteon messages - unsigned char Pmaxkey;//the maximum key that the part receives noteon messages - void setPvolume(char Pvolume); - unsigned char Pkeyshift;//Part keyshift - unsigned char Prcvchn;//from what midi channel it receive commnads - unsigned char Ppanning;//part panning - void setPpanning(char Ppanning); - unsigned char Pvelsns;//velocity sensing (amplitude velocity scale) - unsigned char Pveloffs;//velocity offset - unsigned char Pnoteon;//if the part receives NoteOn messages - unsigned char Pkitmode;//if the kitmode is enabled - unsigned char Pdrummode;//if all keys are mapped and the system is 12tET (used for drums) - - unsigned char Ppolymode;//Part mode - 0=monophonic , 1=polyphonic - unsigned char Plegatomode;// 0=normal, 1=legato - unsigned char Pkeylimit;//how many keys are alowed to be played same time (0=off), the older will be relased - - unsigned char *Pname; //name of the instrument - struct{//instrument additional information - unsigned char Ptype; - unsigned char Pauthor[MAX_INFO_TEXT_SIZE+1]; - unsigned char Pcomments[MAX_INFO_TEXT_SIZE+1]; - } info; - - - REALTYPE *partoutl;//Left channel output of the part - REALTYPE *partoutr;//Right channel output of the part - - REALTYPE *partfxinputl[NUM_PART_EFX+1],*partfxinputr[NUM_PART_EFX+1];//Left and right signal that pass thru part effects; partfxinput l/r [NUM_PART_EFX] is for "no effect" buffer - - enum NoteStatus{KEY_OFF,KEY_PLAYING,KEY_RELASED_AND_SUSTAINED,KEY_RELASED}; - - REALTYPE volume,oldvolumel,oldvolumer;//this is applied by Master - REALTYPE panning;//this is applied by Master, too - - Controller ctl;//Part controllers - - EffectMgr *partefx[NUM_PART_EFX];//insertion part effects (they are part of the instrument) - unsigned char Pefxroute[NUM_PART_EFX];//how the effect's output is routed(to next effect/to out) - bool Pefxbypass[NUM_PART_EFX];//if the effects are bypassed - - - pthread_mutex_t *mutex; - - int lastnote; - - private: - void KillNotePos(int pos); - void RelaseNotePos(int pos); - void MonoMemRenote(); // MonoMem stuff. - - int killallnotes;//is set to 1 if I want to kill all notes - - struct PartNotes{ - NoteStatus status; - int note;//if there is no note playing, the "note"=-1 - int itemsplaying; - struct { - ADnote *adnote; - SUBnote *subnote; - PADnote *padnote; - int sendtoparteffect; - } kititem[NUM_KIT_ITEMS]; - int time; - }; - - int lastpos; // To keep track of previously used pos. - - // MonoMem stuff - std::list<unsigned char> monomemnotes; // A list to remember held notes. - struct { - unsigned char velocity; - int mkeyshift;// I'm not sure masterkeyshift should be remembered. - } monomem[256]; /* 256 is to cover all possible note values. - monomem[] is used in conjunction with the list to - store the velocity and masterkeyshift values of a - given note (the list only store note values). - For example 'monomem[note].velocity' would be the - velocity value of the note 'note'. - */ - - PartNotes partnote[POLIPHONY]; - - REALTYPE *tmpoutl;//used to get the note - REALTYPE *tmpoutr; - - REALTYPE oldfreq;//this is used for portamento - Microtonal *microtonal; - FFTwrapper *fft; + public: + Part(Microtonal *microtonal_,FFTwrapper *fft_,pthread_mutex_t *mutex_); + ~Part(); + + /* Midi commands implemented */ + void NoteOn(unsigned char note,unsigned char velocity,int masterkeyshift); + void NoteOff(unsigned char note); + void AllNotesOff();//panic + void SetController(unsigned int type,int par); + void RelaseSustainedKeys();//this is called when the sustain pedal is relased + void RelaseAllKeys();//this is called on AllNotesOff controller + + /* The synthesizer part output */ + void ComputePartSmps();//Part output + + //instrumentonly: 0 - save all, 1 - save only instrumnet, 2 - save only instrument without the name(used in bank) + + + //saves the instrument settings to a XML file + //returns 0 for ok or <0 if there is an error + int saveXML(char *filename); + int loadXMLinstrument(const char *filename); + + void add2XML(XMLwrapper *xml); + void add2XMLinstrument(XMLwrapper *xml); + + void defaults(); + void defaultsinstrument(); + + void applyparameters(); + + void getfromXML(XMLwrapper *xml); + void getfromXMLinstrument(XMLwrapper *xml); + + void cleanup(); + +// ADnoteParameters *ADPartParameters; +// SUBnoteParameters *SUBPartParameters; + + //the part's kit + struct { + unsigned char Penabled,Pmuted,Pminkey,Pmaxkey; + unsigned char *Pname; + unsigned char Padenabled,Psubenabled,Ppadenabled; + unsigned char Psendtoparteffect; + ADnoteParameters *adpars; + SUBnoteParameters *subpars; + PADnoteParameters *padpars; + } kit[NUM_KIT_ITEMS]; + + + //Part parameters + void setkeylimit(unsigned char Pkeylimit); + void setkititemstatus(int kititem,int Penabled_); + + unsigned char Penabled;//if the part is enabled + unsigned char Pvolume;//part volume + unsigned char Pminkey;//the minimum key that the part receives noteon messages + unsigned char Pmaxkey;//the maximum key that the part receives noteon messages + void setPvolume(char Pvolume); + unsigned char Pkeyshift;//Part keyshift + unsigned char Prcvchn;//from what midi channel it receive commnads + unsigned char Ppanning;//part panning + void setPpanning(char Ppanning); + unsigned char Pvelsns;//velocity sensing (amplitude velocity scale) + unsigned char Pveloffs;//velocity offset + unsigned char Pnoteon;//if the part receives NoteOn messages + unsigned char Pkitmode;//if the kitmode is enabled + unsigned char Pdrummode;//if all keys are mapped and the system is 12tET (used for drums) + + unsigned char Ppolymode;//Part mode - 0=monophonic , 1=polyphonic + unsigned char Plegatomode;// 0=normal, 1=legato + unsigned char Pkeylimit;//how many keys are alowed to be played same time (0=off), the older will be relased + + unsigned char *Pname; //name of the instrument + struct{//instrument additional information + unsigned char Ptype; + unsigned char Pauthor[MAX_INFO_TEXT_SIZE+1]; + unsigned char Pcomments[MAX_INFO_TEXT_SIZE+1]; + } info; + + + REALTYPE *partoutl;//Left channel output of the part + REALTYPE *partoutr;//Right channel output of the part + + REALTYPE *partfxinputl[NUM_PART_EFX+1],*partfxinputr[NUM_PART_EFX+1];//Left and right signal that pass thru part effects; partfxinput l/r [NUM_PART_EFX] is for "no effect" buffer + + enum NoteStatus{KEY_OFF,KEY_PLAYING,KEY_RELASED_AND_SUSTAINED,KEY_RELASED}; + + REALTYPE volume,oldvolumel,oldvolumer;//this is applied by Master + REALTYPE panning;//this is applied by Master, too + + Controller ctl;//Part controllers + + EffectMgr *partefx[NUM_PART_EFX];//insertion part effects (they are part of the instrument) + unsigned char Pefxroute[NUM_PART_EFX];//how the effect's output is routed(to next effect/to out) + bool Pefxbypass[NUM_PART_EFX];//if the effects are bypassed + + + pthread_mutex_t *mutex; + + int lastnote; + + private: + void KillNotePos(int pos); + void RelaseNotePos(int pos); + void MonoMemRenote(); // MonoMem stuff. + + int killallnotes;//is set to 1 if I want to kill all notes + + struct PartNotes{ + NoteStatus status; + int note;//if there is no note playing, the "note"=-1 + int itemsplaying; + struct { + ADnote *adnote; + SUBnote *subnote; + PADnote *padnote; + int sendtoparteffect; + } kititem[NUM_KIT_ITEMS]; + int time; + }; + + int lastpos, lastposb; // To keep track of previously used pos and posb. + bool lastlegatomodevalid; // To keep track of previous legatomodevalid. + + // MonoMem stuff + std::list<unsigned char> monomemnotes; // A list to remember held notes. + struct { + unsigned char velocity; + int mkeyshift;// I'm not sure masterkeyshift should be remembered. + } monomem[256]; /* 256 is to cover all possible note values. + monomem[] is used in conjunction with the list to + store the velocity and masterkeyshift values of a + given note (the list only store note values). + For example 'monomem[note].velocity' would be the + velocity value of the note 'note'. + */ + + PartNotes partnote[POLIPHONY]; + + REALTYPE *tmpoutl;//used to get the note + REALTYPE *tmpoutr; + + REALTYPE oldfreq;//this is used for portamento + Microtonal *microtonal; + FFTwrapper *fft; }; #endif diff --git a/src/Synth/ADnote.C b/src/Synth/ADnote.C @@ -29,13 +29,25 @@ #include "ADnote.h" -ADnote::ADnote(ADnoteParameters *pars,Controller *ctl_,REALTYPE freq,REALTYPE velocity,int portamento_,int midinote_){ +ADnote::ADnote(ADnoteParameters *pars,Controller *ctl_,REALTYPE freq,REALTYPE velocity,int portamento_,int midinote_,bool besilent){ ready=0; - + tmpwave=new REALTYPE [SOUND_BUFFER_SIZE]; bypassl=new REALTYPE [SOUND_BUFFER_SIZE]; bypassr=new REALTYPE [SOUND_BUFFER_SIZE]; + // Initialise some legato-specific vars + Legato.msg=LM_Norm; + Legato.fade.length=(int)(SAMPLE_RATE*0.005);// 0.005 seems ok. + if (Legato.fade.length<1) Legato.fade.length=1;// (if something's fishy) + Legato.fade.step=(1.0/Legato.fade.length); + Legato.decounter=-10; + Legato.param.freq=freq; + Legato.param.vel=velocity; + Legato.param.portamento=portamento_; + Legato.param.midinote=midinote_; + Legato.silent=besilent; + partparams=pars; ctl=ctl_; portamento=portamento_; @@ -183,10 +195,31 @@ ADnote::ADnote(ADnoteParameters *pars,Controller *ctl_,REALTYPE freq,REALTYPE ve // initparameters() stuck together with some lines removed so that it // only alter the already playing note (to perform legato). It is // possible I left stuff that is not required for this. -void ADnote::ADlegatonote(REALTYPE freq, REALTYPE velocity, int portamento_, int midinote_){ +void ADnote::ADlegatonote(REALTYPE freq, REALTYPE velocity, int portamento_, int midinote_, bool externcall){ ADnoteParameters *pars=partparams; //Controller *ctl_=ctl; + // Manage legato stuff + if (externcall) Legato.msg=LM_Norm; + if (Legato.msg!=LM_CatchUp){ + Legato.lastfreq=Legato.param.freq; + Legato.param.freq=freq; + Legato.param.vel=velocity; + Legato.param.portamento=portamento_; + Legato.param.midinote=midinote_; + if (Legato.msg==LM_Norm){ + if (Legato.silent){ + Legato.fade.m=0.0; + Legato.msg=LM_FadeIn; + } else { + Legato.fade.m=1.0; + Legato.msg=LM_FadeOut; + return; + } + } + if (Legato.msg==LM_ToNorm) Legato.msg=LM_Norm; + } + portamento=portamento_; midinote=midinote_; basefreq=freq; @@ -194,7 +227,6 @@ void ADnote::ADlegatonote(REALTYPE freq, REALTYPE velocity, int portamento_, int if (velocity>1.0) velocity=1.0; this->velocity=velocity; - NoteGlobalPar.Detune=getdetune(pars->GlobalPar.PDetuneType ,pars->GlobalPar.PCoarseDetune,pars->GlobalPar.PDetune); bandwidthDetuneMultiplier=pars->getBandwidthDetuneMultiplier(); @@ -1126,7 +1158,74 @@ int ADnote::noteout(REALTYPE *outl,REALTYPE *outr){ }; }; }; - + + + // Apply legato-specific sound signal modifications + if (Legato.silent){ // Silencer + if (Legato.msg!=LM_FadeIn){ + for (i=0;i<SOUND_BUFFER_SIZE;i++){ + outl[i]=0.0; + outr[i]=0.0; + } + } + } + switch (Legato.msg){ + case LM_CatchUp : // Continue the catch-up... + if (Legato.decounter==-10) Legato.decounter=Legato.fade.length; + for (i=0;i<SOUND_BUFFER_SIZE;i++){//Yea, could be done without the loop... + Legato.decounter--; + if (Legato.decounter<1){ + // Catching-up done, we can finally set + // the note to the actual parameters. + Legato.decounter=-10; + Legato.msg=LM_ToNorm; + ADlegatonote(Legato.param.freq, Legato.param.vel, Legato.param.portamento, Legato.param.midinote, false); + break; + } + } + break; + case LM_FadeIn : // Fade-in + if (Legato.decounter==-10) Legato.decounter=Legato.fade.length; + Legato.silent=false; + for (i=0;i<SOUND_BUFFER_SIZE;i++){ + Legato.decounter--; + if (Legato.decounter<1){ + Legato.decounter=-10; + Legato.msg=LM_Norm; + break; + } + Legato.fade.m+=Legato.fade.step; + outl[i]*=Legato.fade.m; + outr[i]*=Legato.fade.m; + } + break; + case LM_FadeOut : // Fade-out, then set the catch-up + if (Legato.decounter==-10) Legato.decounter=Legato.fade.length; + for (i=0;i<SOUND_BUFFER_SIZE;i++){ + Legato.decounter--; + if (Legato.decounter<1){ + for (int j=i;j<SOUND_BUFFER_SIZE;j++){ + outl[j]=0.0; + outr[j]=0.0; + } + Legato.decounter=-10; + Legato.silent=true; + // Fading-out done, now set the catch-up : + Legato.decounter=Legato.fade.length; + Legato.msg=LM_CatchUp; + REALTYPE catchupfreq=Legato.param.freq*(Legato.param.freq/Legato.lastfreq);//This freq should make this now silent note to catch-up (or should I say resync ?) with the heard note for the same length it stayed at the previous freq during the fadeout. + ADlegatonote(catchupfreq, Legato.param.vel, Legato.param.portamento, Legato.param.midinote, false); + break; + } + Legato.fade.m-=Legato.fade.step; + outl[i]*=Legato.fade.m; + outr[i]*=Legato.fade.m; + } + break; + default : break; + } + + // Check if the global amplitude is finished. // If it does, disable the note if (NoteGlobalPar.AmpEnvelope->finished()!=0) { diff --git a/src/Synth/ADnote.h b/src/Synth/ADnote.h @@ -39,10 +39,10 @@ class ADnote{ //ADDitive note public: - ADnote(ADnoteParameters *pars,Controller *ctl_,REALTYPE freq,REALTYPE velocity,int portamento_,int midinote_); + ADnote(ADnoteParameters *pars,Controller *ctl_,REALTYPE freq,REALTYPE velocity,int portamento_,int midinote_,bool besilent);//(gf)Added the besilent parameter to tell it to start silent (if true). ~ADnote(); - void ADlegatonote(REALTYPE freq, REALTYPE velocity, int portamento_, int midinote_); + void ADlegatonote(REALTYPE freq, REALTYPE velocity, int portamento_, int midinote_, bool externcall); int noteout(REALTYPE *outl,REALTYPE *outr); void relasekey(); @@ -252,6 +252,22 @@ class ADnote{ //ADDitive note //how the fine detunes are made bigger or smaller REALTYPE bandwidthDetuneMultiplier; + + // Legato vars + struct { + bool silent; + REALTYPE lastfreq; + LegatoMsg msg; + int decounter; + struct { // Fade In/Out vars + int length; + REALTYPE m, step; + } fade; + struct { // Note parameters + REALTYPE freq, vel; + int portamento, midinote; + } param; + } Legato; }; #endif diff --git a/src/Synth/PADnote.C b/src/Synth/PADnote.C @@ -22,8 +22,21 @@ #include "PADnote.h" #include "../Misc/Config.h" -PADnote::PADnote(PADnoteParameters *parameters, Controller *ctl_,REALTYPE freq, REALTYPE velocity, int portamento_, int midinote){ +PADnote::PADnote(PADnoteParameters *parameters, Controller *ctl_,REALTYPE freq, REALTYPE velocity, int portamento_, int midinote, bool besilent){ ready=0; + + // Initialise some legato-specific vars + Legato.msg=LM_Norm; + Legato.fade.length=(int)(SAMPLE_RATE*0.005);// 0.005 seems ok. + if (Legato.fade.length<1) Legato.fade.length=1;// (if something's fishy) + Legato.fade.step=(1.0/Legato.fade.length); + Legato.decounter=-10; + Legato.param.freq=freq; + Legato.param.vel=velocity; + Legato.param.portamento=portamento_; + Legato.param.midinote=midinote; + Legato.silent=besilent; + pars=parameters; portamento=portamento_; ctl=ctl_; @@ -130,10 +143,31 @@ PADnote::PADnote(PADnoteParameters *parameters, Controller *ctl_,REALTYPE freq, // with some lines removed so that it only alter the already playing // note (to perform legato). It is possible I left stuff that is not // required for this. -void PADnote::PADlegatonote(REALTYPE freq, REALTYPE velocity, int portamento_, int midinote){ +void PADnote::PADlegatonote(REALTYPE freq, REALTYPE velocity, int portamento_, int midinote, bool externcall){ PADnoteParameters *parameters=pars; //Controller *ctl_=ctl; + // Manage legato stuff + if (externcall) Legato.msg=LM_Norm; + if (Legato.msg!=LM_CatchUp){ + Legato.lastfreq=Legato.param.freq; + Legato.param.freq=freq; + Legato.param.vel=velocity; + Legato.param.portamento=portamento_; + Legato.param.midinote=midinote; + if (Legato.msg==LM_Norm){ + if (Legato.silent){ + Legato.fade.m=0.0; + Legato.msg=LM_FadeIn; + } else { + Legato.fade.m=1.0; + Legato.msg=LM_FadeOut; + return; + } + } + if (Legato.msg==LM_ToNorm) Legato.msg=LM_Norm; + } + portamento=portamento_; this->velocity=velocity; finished_=false; @@ -389,7 +423,73 @@ int PADnote::noteout(REALTYPE *outl,REALTYPE *outr){ }; }; - + + // Apply legato-specific sound signal modifications + if (Legato.silent){ // Silencer + if (Legato.msg!=LM_FadeIn){ + for (int i=0;i<SOUND_BUFFER_SIZE;i++){ + outl[i]=0.0; + outr[i]=0.0; + } + } + } + switch (Legato.msg){ + case LM_CatchUp : // Continue the catch-up... + if (Legato.decounter==-10) Legato.decounter=Legato.fade.length; + for (int i=0;i<SOUND_BUFFER_SIZE;i++){//Yea, could be done without the loop... + Legato.decounter--; + if (Legato.decounter<1){ + // Catching-up done, we can finally set + // the note to the actual parameters. + Legato.decounter=-10; + Legato.msg=LM_ToNorm; + PADlegatonote(Legato.param.freq, Legato.param.vel, Legato.param.portamento, Legato.param.midinote, false); + break; + } + } + break; + case LM_FadeIn : // Fade-in + if (Legato.decounter==-10) Legato.decounter=Legato.fade.length; + Legato.silent=false; + for (int i=0;i<SOUND_BUFFER_SIZE;i++){ + Legato.decounter--; + if (Legato.decounter<1){ + Legato.decounter=-10; + Legato.msg=LM_Norm; + break; + } + Legato.fade.m+=Legato.fade.step; + outl[i]*=Legato.fade.m; + outr[i]*=Legato.fade.m; + } + break; + case LM_FadeOut : // Fade-out, then set the catch-up + if (Legato.decounter==-10) Legato.decounter=Legato.fade.length; + for (int i=0;i<SOUND_BUFFER_SIZE;i++){ + Legato.decounter--; + if (Legato.decounter<1){ + for (int j=i;j<SOUND_BUFFER_SIZE;j++){ + outl[j]=0.0; + outr[j]=0.0; + } + Legato.decounter=-10; + Legato.silent=true; + // Fading-out done, now set the catch-up : + Legato.decounter=Legato.fade.length; + Legato.msg=LM_CatchUp; + REALTYPE catchupfreq=Legato.param.freq*(Legato.param.freq/Legato.lastfreq);//This freq should make this now silent note to catch-up (or should I say resync ?) with the heard note for the same length it stayed at the previous freq during the fadeout. + PADlegatonote(catchupfreq, Legato.param.vel, Legato.param.portamento, Legato.param.midinote, false); + break; + } + Legato.fade.m-=Legato.fade.step; + outl[i]*=Legato.fade.m; + outr[i]*=Legato.fade.m; + } + break; + default : break; + } + + // Check if the global amplitude is finished. // If it does, disable the note if (NoteGlobalPar.AmpEnvelope->finished()!=0) { diff --git a/src/Synth/PADnote.h b/src/Synth/PADnote.h @@ -31,10 +31,10 @@ class PADnote{ public: - PADnote(PADnoteParameters *parameters, Controller *ctl_,REALTYPE freq, REALTYPE velocity, int portamento_, int midinote); + PADnote(PADnoteParameters *parameters, Controller *ctl_,REALTYPE freq, REALTYPE velocity, int portamento_, int midinote, bool besilent); ~PADnote(); - void PADlegatonote(REALTYPE freq, REALTYPE velocity, int portamento_, int midinote); + void PADlegatonote(REALTYPE freq, REALTYPE velocity, int portamento_, int midinote, bool externcall); int noteout(REALTYPE *outl,REALTYPE *outr); int finished(); @@ -102,6 +102,22 @@ class PADnote{ REALTYPE globaloldamplitude,globalnewamplitude,velocity,realfreq; REALTYPE *tmpwave; Controller *ctl; + + // Legato vars + struct { + bool silent; + REALTYPE lastfreq; + LegatoMsg msg; + int decounter; + struct { // Fade In/Out vars + int length; + REALTYPE m, step; + } fade; + struct { // Note parameters + REALTYPE freq, vel; + int portamento, midinote; + } param; + } Legato; }; diff --git a/src/Synth/SUBnote.C b/src/Synth/SUBnote.C @@ -27,11 +27,23 @@ #include "SUBnote.h" #include "../Misc/Util.h" -SUBnote::SUBnote(SUBnoteParameters *parameters,Controller *ctl_,REALTYPE freq,REALTYPE velocity,int portamento_,int midinote){ +SUBnote::SUBnote(SUBnoteParameters *parameters,Controller *ctl_,REALTYPE freq,REALTYPE velocity,int portamento_,int midinote,bool besilent){ ready=0; tmpsmp=new REALTYPE[SOUND_BUFFER_SIZE]; tmprnd=new REALTYPE[SOUND_BUFFER_SIZE]; + + // Initialise some legato-specific vars + Legato.msg=LM_Norm; + Legato.fade.length=(int)(SAMPLE_RATE*0.005);// 0.005 seems ok. + if (Legato.fade.length<1) Legato.fade.length=1;// (if something's fishy) + Legato.fade.step=(1.0/Legato.fade.length); + Legato.decounter=-10; + Legato.param.freq=freq; + Legato.param.vel=velocity; + Legato.param.portamento=portamento_; + Legato.param.midinote=midinote; + Legato.silent=besilent; pars=parameters; ctl=ctl_; @@ -147,10 +159,31 @@ SUBnote::SUBnote(SUBnoteParameters *parameters,Controller *ctl_,REALTYPE freq,RE // initparameters(...) stuck together with some lines removed so that // it only alter the already playing note (to perform legato). It is // possible I left stuff that is not required for this. -void SUBnote::SUBlegatonote(REALTYPE freq, REALTYPE velocity, int portamento_, int midinote){ +void SUBnote::SUBlegatonote(REALTYPE freq, REALTYPE velocity, int portamento_, int midinote, bool externcall){ //SUBnoteParameters *parameters=pars; //Controller *ctl_=ctl; + // Manage legato stuff + if (externcall) Legato.msg=LM_Norm; + if (Legato.msg!=LM_CatchUp){ + Legato.lastfreq=Legato.param.freq; + Legato.param.freq=freq; + Legato.param.vel=velocity; + Legato.param.portamento=portamento_; + Legato.param.midinote=midinote; + if (Legato.msg==LM_Norm){ + if (Legato.silent){ + Legato.fade.m=0.0; + Legato.msg=LM_FadeIn; + } else { + Legato.fade.m=1.0; + Legato.msg=LM_FadeOut; + return; + } + } + if (Legato.msg==LM_ToNorm) Legato.msg=LM_Norm; + } + portamento=portamento_; volume=pow(0.1,3.0*(1.0-pars->PVolume/96.0));//-60 dB .. 0 dB @@ -510,7 +543,72 @@ int SUBnote::noteout(REALTYPE *outl,REALTYPE *outr){ oldamplitude=newamplitude; computecurrentparameters(); - + + // Apply legato-specific sound signal modifications + if (Legato.silent){ // Silencer + if (Legato.msg!=LM_FadeIn){ + for (i=0;i<SOUND_BUFFER_SIZE;i++){ + outl[i]=0.0; + outr[i]=0.0; + } + } + } + switch (Legato.msg){ + case LM_CatchUp : // Continue the catch-up... + if (Legato.decounter==-10) Legato.decounter=Legato.fade.length; + for (i=0;i<SOUND_BUFFER_SIZE;i++){//Yea, could be done without the loop... + Legato.decounter--; + if (Legato.decounter<1){ + // Catching-up done, we can finally set + // the note to the actual parameters. + Legato.decounter=-10; + Legato.msg=LM_ToNorm; + SUBlegatonote(Legato.param.freq, Legato.param.vel, Legato.param.portamento, Legato.param.midinote, false); + break; + } + } + break; + case LM_FadeIn : // Fade-in + if (Legato.decounter==-10) Legato.decounter=Legato.fade.length; + Legato.silent=false; + for (i=0;i<SOUND_BUFFER_SIZE;i++){ + Legato.decounter--; + if (Legato.decounter<1){ + Legato.decounter=-10; + Legato.msg=LM_Norm; + break; + } + Legato.fade.m+=Legato.fade.step; + outl[i]*=Legato.fade.m; + outr[i]*=Legato.fade.m; + } + break; + case LM_FadeOut : // Fade-out, then set the catch-up + if (Legato.decounter==-10) Legato.decounter=Legato.fade.length; + for (i=0;i<SOUND_BUFFER_SIZE;i++){ + Legato.decounter--; + if (Legato.decounter<1){ + for (int j=i;j<SOUND_BUFFER_SIZE;j++){ + outl[j]=0.0; + outr[j]=0.0; + } + Legato.decounter=-10; + Legato.silent=true; + // Fading-out done, now set the catch-up : + Legato.decounter=Legato.fade.length; + Legato.msg=LM_CatchUp; + REALTYPE catchupfreq=Legato.param.freq*(Legato.param.freq/Legato.lastfreq);//This freq should make this now silent note to catch-up (or should I say resync ?) with the heard note for the same length it stayed at the previous freq during the fadeout. + SUBlegatonote(catchupfreq, Legato.param.vel, Legato.param.portamento, Legato.param.midinote, false); + break; + } + Legato.fade.m-=Legato.fade.step; + outl[i]*=Legato.fade.m; + outr[i]*=Legato.fade.m; + } + break; + default : break; + } + // Check if the note needs to be computed more if (AmpEnvelope->finished()!=0){ for (i=0;i<SOUND_BUFFER_SIZE;i++) {//fade-out diff --git a/src/Synth/SUBnote.h b/src/Synth/SUBnote.h @@ -31,10 +31,10 @@ class SUBnote{ public: - SUBnote(SUBnoteParameters *parameters,Controller *ctl_,REALTYPE freq,REALTYPE velocity,int portamento_,int midinote); + SUBnote(SUBnoteParameters *parameters,Controller *ctl_,REALTYPE freq,REALTYPE velocity,int portamento_,int midinote,bool besilent); ~SUBnote(); - void SUBlegatonote(REALTYPE freq, REALTYPE velocity, int portamento_, int midinote); + void SUBlegatonote(REALTYPE freq, REALTYPE velocity, int portamento_, int midinote, bool externcall); int noteout(REALTYPE *outl,REALTYPE *outr);//note output,return 0 if the note is finished void relasekey(); @@ -93,6 +93,21 @@ class SUBnote{ int oldpitchwheel,oldbandwidth; REALTYPE globalfiltercenterq; + // Legato vars + struct { + bool silent; + REALTYPE lastfreq; + LegatoMsg msg; + int decounter; + struct { // Fade In/Out vars + int length; + REALTYPE m, step; + } fade; + struct { // Note parameters + REALTYPE freq, vel; + int portamento, midinote; + } param; + } Legato; }; diff --git a/src/globals.h b/src/globals.h @@ -194,6 +194,7 @@ enum MidiControllers{C_NULL=0,C_pitchwheel=1000,C_expression=11,C_panning=10, C_dataentryhi=0x06,C_dataentrylo=0x26,C_nrpnhi=99,C_nrpnlo=98}; +enum LegatoMsg{LM_Norm, LM_FadeIn, LM_FadeOut, LM_CatchUp, LM_ToNorm}; //is like i=(int)(floor(f)) #ifdef ASM_F2I_YES