|
- /*
- ZynAddSubFX - a software synthesizer
-
- Master.cpp - 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 or later) 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 "../Params/LFOParams.h"
- #include "../Effects/EffectMgr.h"
- #include "../DSP/FFTwrapper.h"
-
- #include <stdio.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <iostream>
- #include <algorithm>
- #include <cmath>
-
- #include <unistd.h>
-
- using namespace std;
-
- vuData::vuData(void)
- :outpeakl(0.0f), outpeakr(0.0f), maxoutpeakl(0.0f), maxoutpeakr(0.0f),
- rmspeakl(0.0f), rmspeakr(0.0f), clipped(0)
- {}
-
- static Master* masterInstance = NULL;
-
- Master::Master()
- {
- swaplr = 0;
- off = 0;
- smps = 0;
- bufl = new float[synth->buffersize];
- bufr = new float[synth->buffersize];
-
- pthread_mutex_init(&mutex, NULL);
- pthread_mutex_init(&vumutex, NULL);
- fft = new FFTwrapper(synth->oscilsize);
-
- shutup = 0;
- for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
- vuoutpeakpart[npart] = 1e-9;
- fakepeakpart[npart] = 0;
- }
-
- for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
- part[npart] = new Part(µtonal, fft, &mutex);
-
- //Insertion Effects init
- for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
- insefx[nefx] = new EffectMgr(1, &mutex);
-
- //System Effects init
- for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
- sysefx[nefx] = new EffectMgr(0, &mutex);
-
-
- defaults();
- }
-
- void Master::defaults()
- {
- volume = 1.0f;
- 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)
- setPsysefxvol(npart, nefx, 0);
-
- for(int nefxto = 0; nefxto < NUM_SYS_EFX; ++nefxto)
- setPsysefxsend(nefx, nefxto, 0);
- }
-
- microtonal.defaults();
- ShutUp();
- }
-
- bool Master::mutexLock(lockset request)
- {
- switch(request) {
- case MUTEX_TRYLOCK:
- return !pthread_mutex_trylock(&mutex);
- case MUTEX_LOCK:
- return !pthread_mutex_lock(&mutex);
- case MUTEX_UNLOCK:
- return !pthread_mutex_unlock(&mutex);
- }
- return false;
- }
-
- Master &Master::getInstance()
- {
- if (!masterInstance)
- masterInstance = new Master;
-
- return *masterInstance;
- }
-
- void Master::deleteInstance()
- {
- if (masterInstance)
- {
- delete masterInstance;
- masterInstance = NULL;
- }
- }
-
- /*
- * Note On Messages (velocity=0 for NoteOff)
- */
- void Master::noteOn(char chan, char note, char velocity)
- {
- if(velocity) {
- for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
- if(chan == part[npart]->Prcvchn) {
- fakepeakpart[npart] = velocity * 2;
- if(part[npart]->Penabled)
- part[npart]->NoteOn(note, velocity, keyshift);
- }
- }
- else
- this->noteOff(chan, note);
- HDDRecorder.triggernow();
- }
-
- /*
- * Note Off Messages
- */
- void Master::noteOff(char chan, char note)
- {
- for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
- if((chan == part[npart]->Prcvchn) && part[npart]->Penabled)
- part[npart]->NoteOff(note);
- }
-
- /*
- * Pressure Messages (velocity=0 for NoteOff)
- */
- void Master::polyphonicAftertouch(char chan, char note, char velocity)
- {
- if(velocity) {
- for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
- if(chan == part[npart]->Prcvchn)
- if(part[npart]->Penabled)
- part[npart]->PolyphonicAftertouch(note, velocity, keyshift);
-
- }
- else
- this->noteOff(chan, note);
- }
-
- /*
- * Controllers
- */
- void Master::setController(char chan, 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
- if(type == C_bankselectmsb) { // Change current bank
- if(((unsigned int)par < bank.banks.size())
- && (bank.banks[par].dir != bank.bankfiletitle))
- bank.loadbank(bank.banks[par].dir);
- }
- 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);
- ;
-
- if(type == C_allsoundsoff) { //cleanup insertion/system FX
- for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
- sysefx[nefx]->cleanup();
- for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
- insefx[nefx]->cleanup();
- }
- }
- }
-
- void Master::setProgram(char chan, unsigned int pgm)
- {
- for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
- if(chan == part[npart]->Prcvchn) {
- bank.loadfromslot(pgm, part[npart]);
-
- //Hack to get pad note parameters to update
- //this is not real time safe and makes assumptions about the calling
- //convention of this function...
- pthread_mutex_unlock(&mutex);
- part[npart]->applyparameters();
- pthread_mutex_lock(&mutex);
- }
- }
-
- void Master::vuUpdate(const float *outl, const float *outr)
- {
- //Peak computation (for vumeters)
- vu.outpeakl = 1e-12;
- vu.outpeakr = 1e-12;
- for(int i = 0; i < synth->buffersize; ++i) {
- if(fabs(outl[i]) > vu.outpeakl)
- vu.outpeakl = fabs(outl[i]);
- if(fabs(outr[i]) > vu.outpeakr)
- vu.outpeakr = fabs(outr[i]);
- }
- if((vu.outpeakl > 1.0f) || (vu.outpeakr > 1.0f))
- vu.clipped = 1;
- if(vu.maxoutpeakl < vu.outpeakl)
- vu.maxoutpeakl = vu.outpeakl;
- if(vu.maxoutpeakr < vu.outpeakr)
- vu.maxoutpeakr = vu.outpeakr;
-
- //RMS Peak computation (for vumeters)
- vu.rmspeakl = 1e-12;
- vu.rmspeakr = 1e-12;
- for(int i = 0; i < synth->buffersize; ++i) {
- vu.rmspeakl += outl[i] * outl[i];
- vu.rmspeakr += outr[i] * outr[i];
- }
- vu.rmspeakl = sqrt(vu.rmspeakl / synth->buffersize_f);
- vu.rmspeakr = sqrt(vu.rmspeakr / synth->buffersize_f);
-
- //Part Peak computation (for Part vumeters or fake part vumeters)
- for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
- vuoutpeakpart[npart] = 1.0e-12f;
- if(part[npart]->Penabled != 0) {
- float *outl = part[npart]->partoutl,
- *outr = part[npart]->partoutr;
- for(int i = 0; i < synth->buffersize; ++i) {
- float tmp = fabs(outl[i] + outr[i]);
- if(tmp > vuoutpeakpart[npart])
- vuoutpeakpart[npart] = tmp;
- }
- vuoutpeakpart[npart] *= volume;
- }
- else
- if(fakepeakpart[npart] > 1)
- fakepeakpart[npart]--;
- }
- }
-
- /*
- * 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(float *outl, float *outr)
- {
- //Swaps the Left channel with Right Channel
- if(swaplr)
- swap(outl, outr);
-
- //clean up the output samples (should not be needed?)
- memset(outl, 0, synth->bufferbytes);
- memset(outr, 0, synth->bufferbytes);
-
- //Compute part samples and store them part[npart]->partoutl,partoutr
- for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
- if(part[npart]->Penabled != 0 && !pthread_mutex_trylock(&part[npart]->load_mutex)) {
- part[npart]->ComputePartSmps();
- pthread_mutex_unlock(&part[npart]->load_mutex);
- }
- }
-
- //Insertion effects
- for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
- if(Pinsparts[nefx] >= 0) {
- int efxpart = Pinsparts[nefx];
- if(part[efxpart]->Penabled)
- insefx[nefx]->out(part[efxpart]->partoutl,
- part[efxpart]->partoutr);
- }
-
-
- //Apply the part volumes and pannings (after insertion effects)
- for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
- if(part[npart]->Penabled == 0)
- continue;
-
- Stereo<float> newvol(part[npart]->volume),
- oldvol(part[npart]->oldvolumel,
- part[npart]->oldvolumer);
-
- float pan = part[npart]->panning;
- if(pan < 0.5f)
- newvol.l *= pan * 2.0f;
- else
- newvol.r *= (1.0f - pan) * 2.0f;
-
- //the volume or the panning has changed and needs interpolation
- if(ABOVE_AMPLITUDE_THRESHOLD(oldvol.l, newvol.l)
- || ABOVE_AMPLITUDE_THRESHOLD(oldvol.r, newvol.r)) {
- for(int i = 0; i < synth->buffersize; ++i) {
- Stereo<float> vol(INTERPOLATE_AMPLITUDE(oldvol.l, newvol.l,
- i, synth->buffersize),
- INTERPOLATE_AMPLITUDE(oldvol.r, newvol.r,
- i, synth->buffersize));
- part[npart]->partoutl[i] *= vol.l;
- part[npart]->partoutr[i] *= vol.r;
- }
- part[npart]->oldvolumel = newvol.l;
- part[npart]->oldvolumer = newvol.r;
- }
- else
- for(int i = 0; i < synth->buffersize; ++i) { //the volume did not changed
- part[npart]->partoutl[i] *= newvol.l;
- part[npart]->partoutr[i] *= newvol.r;
- }
- }
-
-
- //System effects
- for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
- if(sysefx[nefx]->geteffect() == 0)
- continue; //the effect is disabled
-
- float *tmpmixl = getTmpBuffer();
- float *tmpmixr = getTmpBuffer();
- //Clean up the samples used by the system effects
- memset(tmpmixl, 0, synth->bufferbytes);
- memset(tmpmixr, 0, synth->bufferbytes);
-
- //Mix the channels according to the part settings about System Effect
- for(int 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
- const float vol = sysefxvol[nefx][npart];
- for(int i = 0; i < synth->buffersize; ++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) {
- const float vol = sysefxsend[nefxfrom][nefx];
- for(int i = 0; i < synth->buffersize; ++i) {
- tmpmixl[i] += sysefx[nefxfrom]->efxoutl[i] * vol;
- tmpmixr[i] += sysefx[nefxfrom]->efxoutr[i] * vol;
- }
- }
-
- sysefx[nefx]->out(tmpmixl, tmpmixr);
-
- //Add the System Effect to sound output
- const float outvol = sysefx[nefx]->sysefxgetvolume();
- for(int i = 0; i < synth->buffersize; ++i) {
- outl[i] += tmpmixl[i] * outvol;
- outr[i] += tmpmixr[i] * outvol;
- }
-
- returnTmpBuffer(tmpmixl);
- returnTmpBuffer(tmpmixr);
- }
-
- //Mix all parts
- for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
- if(part[npart]->Penabled) //only mix active parts
- for(int i = 0; i < synth->buffersize; ++i) { //the volume did not changed
- outl[i] += part[npart]->partoutl[i];
- outr[i] += part[npart]->partoutr[i];
- }
-
- //Insertion effects for Master Out
- for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
- if(Pinsparts[nefx] == -2)
- insefx[nefx]->out(outl, outr);
-
-
- //Master Volume
- for(int i = 0; i < synth->buffersize; ++i) {
- outl[i] *= volume;
- outr[i] *= volume;
- }
-
- if(!pthread_mutex_trylock(&vumutex)) {
- vuUpdate(outl, outr);
- pthread_mutex_unlock(&vumutex);
- }
-
- //Shutup if it is asked (with fade-out)
- if(shutup) {
- for(int i = 0; i < synth->buffersize; ++i) {
- float tmp = (synth->buffersize_f - i) / synth->buffersize_f;
- outl[i] *= tmp;
- outr[i] *= tmp;
- }
- ShutUp();
- }
-
- //update the LFO's time
- LFOParams::time++;
-
- dump.inctick();
- }
-
- //TODO review the respective code from yoshimi for this
- //If memory serves correctly, libsamplerate was used
- void Master::GetAudioOutSamples(size_t nsamples,
- unsigned samplerate,
- float *outl,
- float *outr)
- {
- off_t out_off = 0;
-
- //Fail when resampling rather than doing a poor job
- if(synth->samplerate != samplerate) {
- printf("darn it: %d vs %d\n", synth->samplerate, samplerate);
- return;
- }
-
- while(nsamples) {
- //use all available samples
- if(nsamples >= smps) {
- memcpy(outl + out_off, bufl + off, sizeof(float) * smps);
- memcpy(outr + out_off, bufr + off, sizeof(float) * smps);
- nsamples -= smps;
-
- //generate samples
- AudioOut(bufl, bufr);
- off = 0;
- out_off += smps;
- smps = synth->buffersize;
- }
- else { //use some samples
- memcpy(outl + out_off, bufl + off, sizeof(float) * nsamples);
- memcpy(outr + out_off, bufr + off, sizeof(float) * nsamples);
- smps -= nsamples;
- off += nsamples;
- nsamples = 0;
- }
- }
- }
-
- Master::~Master()
- {
- delete []bufl;
- delete []bufr;
-
- 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 fft;
-
- pthread_mutex_destroy(&mutex);
- pthread_mutex_destroy(&vumutex);
- }
-
-
- /*
- * Parameter control
- */
- void Master::setPvolume(char Pvolume_)
- {
- Pvolume = Pvolume_;
- volume = dB2rap((Pvolume - 96.0f) / 96.0f * 40.0f);
- }
-
- 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] = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f);
- }
-
- void Master::setPsysefxsend(int Pefxfrom, int Pefxto, char Pvol)
- {
- Psysefxsend[Pefxfrom][Pefxto] = Pvol;
- sysefxsend[Pefxfrom][Pefxto] = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f);
- }
-
-
- /*
- * 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()
- {
- pthread_mutex_lock(&vumutex);
- vu.outpeakl = 1e-9;
- vu.outpeakr = 1e-9;
- vu.maxoutpeakl = 1e-9;
- vu.maxoutpeakr = 1e-9;
- vu.clipped = 0;
- pthread_mutex_unlock(&vumutex);
- }
-
- vuData Master::getVuData()
- {
- vuData tmp;
- pthread_mutex_lock(&vumutex);
- tmp = vu;
- pthread_mutex_unlock(&vumutex);
- return tmp;
- }
-
- void Master::applyparameters(bool lockmutex)
- {
- for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
- part[npart]->applyparameters(lockmutex);
- }
-
- 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");
-
- pthread_mutex_lock(&mutex);
- add2XML(xml);
- pthread_mutex_unlock(&mutex);
-
- 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;
-
- pthread_mutex_lock(&mutex);
- getfromXML(xml);
- pthread_mutex_unlock(&mutex);
-
- xml->exitbranch();
-
- delete (xml);
- }
-
- int Master::saveXML(const 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(const 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();
- }
- }
|