/* 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 <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> 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;npart<NUM_MIDI_PARTS;npart++) { vuoutpeakpart[npart]=1e-9; fakepeakpart[npart]=0; }; memset(audiooutl, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE); memset(audiooutr, 0, sizeof(REALTYPE) * SOUND_BUFFER_SIZE); for (int npart=0;npart<NUM_MIDI_PARTS;npart++) part[npart]=new Part(µtonal,fft,this); //Insertion Effects init for (int nefx=0;nefx<NUM_INS_EFX;nefx++) insefx[nefx]=new EffectMgr(1,this); //System Effects init for (int nefx=0;nefx<NUM_SYS_EFX;nefx++) { sysefx[nefx]=new EffectMgr(0,this); }; defaults(); }; void Master::defaults(){ volume=1.0; setPvolume(80); setPkeyshift(64); for (int npart=0;npart<NUM_MIDI_PARTS;npart++){ part[npart]->defaults(); part[npart]->Prcvchn=npart%NUM_MIDI_CHANNELS; }; partonoff(0,1);//enable the first part for (int nefx=0;nefx<NUM_INS_EFX;nefx++) { insefx[nefx]->defaults(); Pinsparts[nefx]=-1; }; //System Effects init for (int nefx=0;nefx<NUM_SYS_EFX;nefx++) { sysefx[nefx]->defaults(); for (int npart=0;npart<NUM_MIDI_PARTS;npart++){ if (nefx==0) setPsysefxvol(npart,nefx,64); else setPsysefxvol(npart,nefx,0); }; for (int nefxto=0;nefxto<NUM_SYS_EFX;nefxto++) setPsysefxsend(nefx,nefxto,0); }; sysefx[0]->changeeffect(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;npart<NUM_MIDI_PARTS;npart++){ if (chan==part[npart]->Prcvchn){ 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;npart<NUM_MIDI_PARTS;npart++){ if ((chan==part[npart]->Prcvchn) && (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 (parlo<NUM_SYS_EFX) { sysefx[parlo]->seteffectpar_nolock(valhi,vallo); }; break; case 0x08://Insertion Effects if (parlo<NUM_INS_EFX) { insefx[parlo]->seteffectpar_nolock(valhi,vallo); }; break; }; }; } else {//other controllers for (int npart=0;npart<NUM_MIDI_PARTS;npart++){//Send the controller to all part assigned to the channel if ((chan==part[npart]->Prcvchn) && (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;nefx<NUM_INS_EFX;nefx++){ if (Pinsparts[nefx]==npart) { insefx[nefx]->cleanup(); }; }; } 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; npart<NUM_MIDI_PARTS; npart++) if (part[npart]->Penabled != 0) part[npart]->ComputePartSmps(); //Insertion effects for (nefx=0;nefx<NUM_INS_EFX;nefx++){ if (Pinsparts[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;i<SOUND_BUFFER_SIZE;i++) { REALTYPE vol_l = INTERPOLATE_AMPLITUDE(oldvol_l,newvol_l,i,SOUND_BUFFER_SIZE); REALTYPE vol_r = INTERPOLATE_AMPLITUDE(oldvol_r,newvol_r,i,SOUND_BUFFER_SIZE); part[npart]->partoutl[i]*=vol_l; part[npart]->partoutr[i]*=vol_r; } part[npart]->oldvolumel=newvol_l; part[npart]->oldvolumer=newvol_r; } else { for (i=0;i<SOUND_BUFFER_SIZE;i++) {//the volume did not changed part[npart]->partoutl[i] *= newvol_l; part[npart]->partoutr[i] *= newvol_r; } } } //System effects for (nefx=0;nefx<NUM_SYS_EFX;nefx++){ if (sysefx[nefx]->geteffect()==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;npart<NUM_MIDI_PARTS;npart++){ //skip if the part has no output to effect if (Psysefxvol[nefx][npart]==0) continue; //skip if the part is disabled if (part[npart]->Penabled==0) continue; //the output volume of each part to system effect REALTYPE vol=sysefxvol[nefx][npart]; for (i=0;i<SOUND_BUFFER_SIZE;i++) { tmpmixl[i]+=part[npart]->partoutl[i]*vol; tmpmixr[i]+=part[npart]->partoutr[i]*vol; }; }; // system effect send to next ones for (int nefxfrom=0;nefxfrom<nefx;nefxfrom++){ if (Psysefxsend[nefxfrom][nefx]!=0){ REALTYPE v=sysefxsend[nefxfrom][nefx]; for (i=0;i<SOUND_BUFFER_SIZE;i++) { tmpmixl[i]+=sysefx[nefxfrom]->efxoutl[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;i<SOUND_BUFFER_SIZE;i++) { outl[i]+=tmpmixl[i]*outvol; outr[i]+=tmpmixr[i]*outvol; }; }; //Mix all parts for (npart=0;npart<NUM_MIDI_PARTS;npart++){ for (i=0;i<SOUND_BUFFER_SIZE;i++) { //the volume did not changed outl[i] += part[npart]->partoutl[i]; outr[i] += part[npart]->partoutr[i]; } } //Insertion effects for Master Out for (nefx=0;nefx<NUM_INS_EFX;nefx++) { if (Pinsparts[nefx] == -2) insefx[nefx]->out(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;i<SOUND_BUFFER_SIZE;i++) { if (fabs(outl[i])>vuoutpeakl) vuoutpeakl=fabs(outl[i]); if (fabs(outr[i])>vuoutpeakr) vuoutpeakr=fabs(outr[i]); }; if ((vuoutpeakl>1.0)||(vuoutpeakr>1.0)) vuclipped=1; if (vumaxoutpeakl<vuoutpeakl) vumaxoutpeakl=vuoutpeakl; if (vumaxoutpeakr<vuoutpeakr) vumaxoutpeakr=vuoutpeakr; //RMS Peak computation (for vumeters) vurmspeakl=1e-12;vurmspeakr=1e-12; for (i=0;i<SOUND_BUFFER_SIZE;i++) { vurmspeakl+=outl[i]*outl[i]; vurmspeakr+=outr[i]*outr[i]; }; vurmspeakl=sqrt(vurmspeakl/SOUND_BUFFER_SIZE); vurmspeakr=sqrt(vurmspeakr/SOUND_BUFFER_SIZE); //Part Peak computation (for Part vumeters or fake part vumeters) for (npart=0;npart<NUM_MIDI_PARTS;npart++){ vuoutpeakpart[npart]=1.0e-12; if (part[npart]->Penabled!=0) { REALTYPE *outl=part[npart]->partoutl, *outr=part[npart]->partoutr; for (i=0;i<SOUND_BUFFER_SIZE;i++) { REALTYPE tmp=fabs(outl[i]+outr[i]); if (tmp>vuoutpeakpart[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;npart<NUM_MIDI_PARTS;npart++) delete (part[npart]); for (int nefx=0;nefx<NUM_INS_EFX;nefx++) delete (insefx[nefx]); for (int nefx=0;nefx<NUM_SYS_EFX;nefx++) delete (sysefx[nefx]); delete [] audiooutl; delete [] audiooutr; delete [] tmpmixl; delete [] tmpmixr; delete (fft); }; /* * Parameter control */ void Master::setPvolume(char Pvolume_){ Pvolume=Pvolume_; volume=dB2rap((Pvolume-96.0)/96.0*40.0); }; void Master::setPkeyshift(char Pkeyshift_){ Pkeyshift=Pkeyshift_; keyshift=(int)Pkeyshift-64; }; void Master::setPsysefxvol(int Ppart,int Pefx,char Pvol){ Psysefxvol[Pefx][Ppart]=Pvol; sysefxvol[Pefx][Ppart]=pow(0.1,(1.0-Pvol/96.0)*2.0); }; void Master::setPsysefxsend(int Pefxfrom,int Pefxto,char Pvol){ Psysefxsend[Pefxfrom][Pefxto]=Pvol; sysefxsend[Pefxfrom][Pefxto]=pow(0.1,(1.0-Pvol/96.0)*2.0); }; /* * Panic! (Clean up all parts and effects) */ void Master::ShutUp(){ for (int npart=0;npart<NUM_MIDI_PARTS;npart++) { part[npart]->cleanup(); fakepeakpart[npart]=0; }; for (int nefx=0;nefx<NUM_INS_EFX;nefx++) insefx[nefx]->cleanup(); for (int nefx=0;nefx<NUM_SYS_EFX;nefx++) sysefx[nefx]->cleanup(); 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;npart<NUM_MIDI_PARTS;npart++){ part[npart]->applyparameters(); }; }; 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;npart<NUM_MIDI_PARTS;npart++){ xml->beginbranch("PART",npart); part[npart]->add2XML(xml); xml->endbranch(); }; xml->beginbranch("SYSTEM_EFFECTS"); for (int nefx=0;nefx<NUM_SYS_EFX;nefx++){ xml->beginbranch("SYSTEM_EFFECT",nefx); xml->beginbranch("EFFECT"); sysefx[nefx]->add2XML(xml); xml->endbranch(); for (int pefx=0;pefx<NUM_MIDI_PARTS;pefx++){ xml->beginbranch("VOLUME",pefx); xml->addpar("vol",Psysefxvol[nefx][pefx]); xml->endbranch(); }; for (int tonefx=nefx+1;tonefx<NUM_SYS_EFX;tonefx++){ xml->beginbranch("SENDTO",tonefx); xml->addpar("send_vol",Psysefxsend[nefx][tonefx]); xml->endbranch(); }; xml->endbranch(); }; xml->endbranch(); xml->beginbranch("INSERTION_EFFECTS"); for (int nefx=0;nefx<NUM_INS_EFX;nefx++){ xml->beginbranch("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;npart<NUM_MIDI_PARTS;npart++){ if (xml->enterbranch("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;nefx<NUM_SYS_EFX;nefx++){ if (xml->enterbranch("SYSTEM_EFFECT",nefx)==0) continue; if (xml->enterbranch("EFFECT")){ sysefx[nefx]->getfromXML(xml); xml->exitbranch(); }; for (int partefx=0;partefx<NUM_MIDI_PARTS;partefx++){ if (xml->enterbranch("VOLUME",partefx)==0) continue; setPsysefxvol(partefx,nefx,xml->getpar127("vol",Psysefxvol[partefx][nefx])); xml->exitbranch(); }; for (int tonefx=nefx+1;tonefx<NUM_SYS_EFX;tonefx++){ if (xml->enterbranch("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;nefx<NUM_INS_EFX;nefx++){ if (xml->enterbranch("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(); }; };