/* ZynAddSubFX - a software synthesizer Master.C - It sends Midi Messages to Parts, receives samples from parts, process them with system/insertion effects and mix them 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 #include #include #include Master::Master(){ swaplr=0; busy = false; fft=new FFTwrapper(OSCIL_SIZE); tmpmixl=new REALTYPE[SOUND_BUFFER_SIZE]; tmpmixr=new REALTYPE[SOUND_BUFFER_SIZE]; audiooutl=new REALTYPE[SOUND_BUFFER_SIZE]; audiooutr=new REALTYPE[SOUND_BUFFER_SIZE]; ksoundbuffersamples=0; ksoundbuffersamplelow=0.0; oldsamplel=0.0;oldsampler=0.0; shutup=0; for (int npart=0;npartdefaults(); part[npart]->Prcvchn=npart%NUM_MIDI_CHANNELS; }; partonoff(0,1);//enable the first part for (int nefx=0;nefxdefaults(); Pinsparts[nefx]=-1; }; //System Effects init for (int nefx=0;nefxdefaults(); for (int npart=0;npartchangeeffect(1); microtonal.defaults(); ShutUp(); }; /* * Note On Messages (velocity=0 for NoteOff) */ void Master::NoteOn(unsigned char chan,unsigned char note,unsigned char velocity){ dump.dumpnote(chan,note,velocity); noteon(chan,note,velocity); }; /* * Internal Note On (velocity=0 for NoteOff) */ void Master::noteon(unsigned char chan,unsigned char note,unsigned char velocity){ int npart; if (velocity!=0){ for (npart=0;npartPrcvchn){ fakepeakpart[npart]=velocity*2; if (part[npart]->Penabled!=0) part[npart]->NoteOn(note,velocity,keyshift); }; }; }else{ this->NoteOff(chan,note); }; HDDRecorder.triggernow(); }; /* * Note Off Messages */ void Master::NoteOff(unsigned char chan,unsigned char note){ dump.dumpnote(chan,note,0); noteoff(chan,note); }; /* * Internal Note Off */ void Master::noteoff(unsigned char chan,unsigned char note){ int npart; for (npart=0;npartPrcvchn) && (part[npart]->Penabled!=0)) part[npart]->NoteOff(note); }; }; /* * Controllers */ void Master::SetController(unsigned char chan,unsigned int type,int par){ dump.dumpcontroller(chan,type,par); setcontroller(chan,type,par); }; /* * Internal Controllers */ void Master::setcontroller(unsigned char chan,unsigned int type,int par){ if ((type==C_dataentryhi)||(type==C_dataentrylo)|| (type==C_nrpnhi)||(type==C_nrpnlo)){//Process RPN and NRPN by the Master (ignore the chan) ctl.setparameternumber(type,par); int parhi=-1,parlo=-1,valhi=-1,vallo=-1; if (ctl.getnrpn(&parhi,&parlo,&valhi,&vallo)==0){//this is NRPN //fprintf(stderr,"rcv. NRPN: %d %d %d %d\n",parhi,parlo,valhi,vallo); switch (parhi){ case 0x04://System Effects if (parloseteffectpar_nolock(valhi,vallo); }; break; case 0x08://Insertion Effects if (parloseteffectpar_nolock(valhi,vallo); }; break; }; }; } else {//other controllers for (int npart=0;npartPrcvchn) && (part[npart]->Penabled!=0)) part[npart]->SetController(type,par); }; }; }; /* * Enable/Disable a part */ void Master::partonoff(int npart,int what){ if (npart>=NUM_MIDI_PARTS) return; if (what==0){//disable part fakepeakpart[npart]=0; part[npart]->Penabled=0; part[npart]->cleanup(); for (int nefx=0;nefxcleanup(); }; }; } else {//enabled part[npart]->Penabled=1; fakepeakpart[npart]=0; }; }; /* * Master audio out (the final sound) */ void Master::AudioOut(REALTYPE* outl, REALTYPE* outr) { int i,npart,nefx; //Swaps the Left channel with Right Channel (if it is asked for) if (swaplr != 0) { REALTYPE *tmp=outl; outl = outr; outr = tmp; } //clean up the output samples memset(outl, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE); memset(outr, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE); //Compute part samples and store them part[npart]->partoutl,partoutr for (npart=0; npartPenabled != 0) part[npart]->ComputePartSmps(); //Insertion effects for (nefx=0;nefx=0) { int efxpart=Pinsparts[nefx]; if (part[efxpart]->Penabled!=0) insefx[nefx]->out(part[efxpart]->partoutl,part[efxpart]->partoutr); } } //Apply the part volumes and pannings (after insertion effects) for (npart = 0; npart < NUM_MIDI_PARTS; npart++) { if (part[npart]->Penabled==0) continue; REALTYPE newvol_l=part[npart]->volume; REALTYPE newvol_r=part[npart]->volume; REALTYPE oldvol_l=part[npart]->oldvolumel; REALTYPE oldvol_r=part[npart]->oldvolumer; REALTYPE pan=part[npart]->panning; if (pan < 0.5) newvol_l *= pan*2.0; else newvol_r *= (1.0-pan)*2.0; if (ABOVE_AMPLITUDE_THRESHOLD(oldvol_l,newvol_l)|| ABOVE_AMPLITUDE_THRESHOLD(oldvol_r,newvol_r)){//the volume or the panning has changed and needs interpolation for (i=0;ipartoutl[i]*=vol_l; part[npart]->partoutr[i]*=vol_r; } part[npart]->oldvolumel=newvol_l; part[npart]->oldvolumer=newvol_r; } else { for (i=0;ipartoutl[i] *= newvol_l; part[npart]->partoutr[i] *= newvol_r; } } } //System effects for (nefx=0;nefxgeteffect()==0) continue;//the effect is disabled //Clean up the samples used by the system effects memset(tmpmixl, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE); memset(tmpmixr, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE); //Mix the channels according to the part settings about System Effect for (npart=0;npartPenabled==0) continue; //the output volume of each part to system effect REALTYPE vol=sysefxvol[nefx][npart]; for (i=0;ipartoutl[i]*vol; tmpmixr[i]+=part[npart]->partoutr[i]*vol; }; }; // system effect send to next ones for (int nefxfrom=0;nefxfromefxoutl[i]*v; tmpmixr[i]+=sysefx[nefxfrom]->efxoutr[i]*v; }; }; }; sysefx[nefx]->out(tmpmixl,tmpmixr); //Add the System Effect to sound output REALTYPE outvol=sysefx[nefx]->sysefxgetvolume(); for (i=0;ipartoutl[i]; outr[i] += part[npart]->partoutr[i]; } } //Insertion effects for Master Out for (nefx=0;nefxout(outl, outr); } //Master Volume for (i = 0; i < SOUND_BUFFER_SIZE; i++) { outl[i] *= volume; outr[i] *= volume; } //Peak computation (for vumeters) vuoutpeakl=1e-12;vuoutpeakr=1e-12; for (i=0;ivuoutpeakl) vuoutpeakl=fabs(outl[i]); if (fabs(outr[i])>vuoutpeakr) vuoutpeakr=fabs(outr[i]); }; if ((vuoutpeakl>1.0)||(vuoutpeakr>1.0)) vuclipped=1; if (vumaxoutpeaklPenabled!=0) { REALTYPE *outl=part[npart]->partoutl, *outr=part[npart]->partoutr; for (i=0;ivuoutpeakpart[npart]) vuoutpeakpart[npart]=tmp; }; vuoutpeakpart[npart]*=volume; } else { if (fakepeakpart[npart]>1) fakepeakpart[npart]--; }; }; //Shutup if it is asked (with fade-out) if (shutup != 0) { for (i = 0; i < SOUND_BUFFER_SIZE; i++) { REALTYPE tmp=(SOUND_BUFFER_SIZE-i)/(REALTYPE) SOUND_BUFFER_SIZE; outl[i] *= tmp; outr[i] *= tmp; } ShutUp(); } //update the LFO's time LFOParams::time++; if (HDDRecorder.recording()) HDDRecorder.recordbuffer(outl,outr); dump.inctick(); }; //--------------------------------------------------------- // GetAudioOutSamples //--------------------------------------------------------- void Master::GetAudioOutSamples(int nsamples, REALTYPE* outl, REALTYPE* outr) { int dstOffset = 0; while (nsamples) { if (ksoundbuffersamples <= 0) { AudioOut(audiooutl, audiooutr); ksoundbuffersamples = SOUND_BUFFER_SIZE; } int n = nsamples > ksoundbuffersamples ? ksoundbuffersamples : nsamples; int srcOffset = SOUND_BUFFER_SIZE - ksoundbuffersamples; memcpy(outl + dstOffset, audiooutl + srcOffset, n * sizeof(REALTYPE)); memcpy(outr + dstOffset, audiooutr + srcOffset, n * sizeof(REALTYPE)); nsamples -= n; dstOffset += n; ksoundbuffersamples -= n; } } Master::~Master(){ for (int npart=0;npartcleanup(); fakepeakpart[npart]=0; }; for (int nefx=0;nefxcleanup(); for (int nefx=0;nefxcleanup(); vuresetpeaks(); shutup=0; }; /* * Reset peaks and clear the "cliped" flag (for VU-meter) */ void Master::vuresetpeaks(){ vuoutpeakl=1e-9;vuoutpeakr=1e-9;vumaxoutpeakl=1e-9;vumaxoutpeakr=1e-9; vuclipped=0; }; void Master::applyparameters(){ for (int npart=0;npartapplyparameters(); }; }; void Master::add2XML(XMLwrapper *xml){ xml->addpar("volume",Pvolume); xml->addpar("key_shift",Pkeyshift); xml->addparbool("nrpn_receive",ctl.NRPN.receive); xml->beginbranch("MICROTONAL"); microtonal.add2XML(xml); xml->endbranch(); for (int npart=0;npartbeginbranch("PART",npart); part[npart]->add2XML(xml); xml->endbranch(); }; xml->beginbranch("SYSTEM_EFFECTS"); for (int nefx=0;nefxbeginbranch("SYSTEM_EFFECT",nefx); xml->beginbranch("EFFECT"); sysefx[nefx]->add2XML(xml); xml->endbranch(); for (int pefx=0;pefxbeginbranch("VOLUME",pefx); xml->addpar("vol",Psysefxvol[nefx][pefx]); xml->endbranch(); }; for (int tonefx=nefx+1;tonefxbeginbranch("SENDTO",tonefx); xml->addpar("send_vol",Psysefxsend[nefx][tonefx]); xml->endbranch(); }; xml->endbranch(); }; xml->endbranch(); xml->beginbranch("INSERTION_EFFECTS"); for (int nefx=0;nefxbeginbranch("INSERTION_EFFECT",nefx); xml->addpar("part",Pinsparts[nefx]); xml->beginbranch("EFFECT"); insefx[nefx]->add2XML(xml); xml->endbranch(); xml->endbranch(); }; xml->endbranch(); }; int Master::getalldata(char **data){ XMLwrapper *xml=new XMLwrapper(); xml->beginbranch("MASTER"); busy = true; add2XML(xml); busy = false; xml->endbranch(); *data=xml->getXMLdata(); delete (xml); return(strlen(*data)+1); }; void Master::putalldata(char *data,int size){ XMLwrapper *xml=new XMLwrapper(); if (!xml->putXMLdata(data)) { delete(xml); return; }; if (xml->enterbranch("MASTER")==0) return; busy = true; getfromXML(xml); busy = false; xml->exitbranch(); delete(xml); }; int Master::saveXML(char *filename){ XMLwrapper *xml=new XMLwrapper(); xml->beginbranch("MASTER"); add2XML(xml); xml->endbranch(); int result=xml->saveXMLfile(filename); delete (xml); return(result); }; int Master::loadXML(char *filename){ XMLwrapper *xml=new XMLwrapper(); if (xml->loadXMLfile(filename)<0) { delete(xml); return(-1); }; if (xml->enterbranch("MASTER")==0) return(-10); getfromXML(xml); xml->exitbranch(); delete(xml); return(0); }; void Master::getfromXML(XMLwrapper *xml){ setPvolume(xml->getpar127("volume",Pvolume)); setPkeyshift(xml->getpar127("key_shift",Pkeyshift)); ctl.NRPN.receive=xml->getparbool("nrpn_receive",ctl.NRPN.receive); part[0]->Penabled=0; for (int npart=0;npartenterbranch("PART",npart)==0) continue; part[npart]->getfromXML(xml); xml->exitbranch(); }; if (xml->enterbranch("MICROTONAL")){ microtonal.getfromXML(xml); xml->exitbranch(); }; sysefx[0]->changeeffect(0); if (xml->enterbranch("SYSTEM_EFFECTS")){ for (int nefx=0;nefxenterbranch("SYSTEM_EFFECT",nefx)==0) continue; if (xml->enterbranch("EFFECT")){ sysefx[nefx]->getfromXML(xml); xml->exitbranch(); }; for (int partefx=0;partefxenterbranch("VOLUME",partefx)==0) continue; setPsysefxvol(partefx,nefx,xml->getpar127("vol",Psysefxvol[partefx][nefx])); xml->exitbranch(); }; for (int tonefx=nefx+1;tonefxenterbranch("SENDTO",tonefx)==0) continue; setPsysefxsend(nefx,tonefx,xml->getpar127("send_vol",Psysefxsend[nefx][tonefx])); xml->exitbranch(); }; xml->exitbranch(); }; xml->exitbranch(); }; if (xml->enterbranch("INSERTION_EFFECTS")){ for (int nefx=0;nefxenterbranch("INSERTION_EFFECT",nefx)==0) continue; Pinsparts[nefx]=xml->getpar("part",Pinsparts[nefx],-2,NUM_MIDI_PARTS); if (xml->enterbranch("EFFECT")){ insefx[nefx]->getfromXML(xml); xml->exitbranch(); }; xml->exitbranch(); }; xml->exitbranch(); }; };