/* ZynAddSubFX - a software synthesizer ADnote.cpp - The "additive" synthesizer 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 #include #include #include #include "../globals.h" #include "../Misc/Util.h" #include "ADnote.h" ADnote::ADnote(ADnoteParameters *pars, Controller *ctl_, REALTYPE freq, REALTYPE velocity, int portamento_, int midinote_, bool besilent) { ready = 0; tmpwavel = new REALTYPE [SOUND_BUFFER_SIZE]; tmpwaver = 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_; midinote = midinote_; NoteEnabled = ON; basefreq = freq; if(velocity > 1.0) velocity = 1.0; this->velocity = velocity; time = 0.0; stereo = pars->GlobalPar.PStereo; NoteGlobalPar.Detune = getdetune(pars->GlobalPar.PDetuneType, pars->GlobalPar.PCoarseDetune, pars->GlobalPar.PDetune); bandwidthDetuneMultiplier = pars->getBandwidthDetuneMultiplier(); if(pars->GlobalPar.PPanning == 0) NoteGlobalPar.Panning = RND; else NoteGlobalPar.Panning = pars->GlobalPar.PPanning / 128.0; NoteGlobalPar.FilterCenterPitch = pars->GlobalPar.GlobalFilter->getfreq() //center freq + pars->GlobalPar.PFilterVelocityScale / 127.0 * 6.0 //velocity sensing * (VelF(velocity, pars->GlobalPar. PFilterVelocityScaleFunction) - 1); if(pars->GlobalPar.PPunchStrength != 0) { NoteGlobalPar.Punch.Enabled = 1; NoteGlobalPar.Punch.t = 1.0; //start from 1.0 and to 0.0 NoteGlobalPar.Punch.initialvalue = ((pow(10, 1.5 * pars->GlobalPar.PPunchStrength / 127.0) - 1.0) * VelF(velocity, pars->GlobalPar.PPunchVelocitySensing)); REALTYPE time = pow(10, 3.0 * pars->GlobalPar.PPunchTime / 127.0) / 10000.0; //0.1 .. 100 ms REALTYPE stretch = pow(440.0 / freq, pars->GlobalPar.PPunchStretch / 64.0); NoteGlobalPar.Punch.dt = 1.0 / (time * SAMPLE_RATE * stretch); } else NoteGlobalPar.Punch.Enabled = 0; for(int nvoice = 0; nvoice < NUM_VOICES; nvoice++) { pars->VoicePar[nvoice].OscilSmp->newrandseed(rand()); NoteVoicePar[nvoice].OscilSmp = NULL; NoteVoicePar[nvoice].FMSmp = NULL; NoteVoicePar[nvoice].VoiceOut = NULL; NoteVoicePar[nvoice].FMVoice = -1; unison_size[nvoice] = 1; if(pars->VoicePar[nvoice].Enabled == 0) { NoteVoicePar[nvoice].Enabled = OFF; continue; //the voice is disabled } unison_stereo_spread[nvoice] = pars->VoicePar[nvoice].Unison_stereo_spread / 127.0; int unison = pars->VoicePar[nvoice].Unison_size; if(unison < 1) unison = 1; //compute unison unison_size[nvoice] = unison; unison_base_freq_rap[nvoice] = new REALTYPE[unison]; unison_freq_rap[nvoice] = new REALTYPE[unison]; unison_invert_phase[nvoice] = new bool[unison]; REALTYPE unison_spread = pars->getUnisonFrequencySpreadCents( nvoice); REALTYPE unison_real_spread = pow(2.0, (unison_spread * 0.5) / 1200.0); REALTYPE unison_vibratto_a = pars->VoicePar[nvoice].Unison_vibratto / 127.0; //0.0 .. 1.0 switch(unison) { case 1: unison_base_freq_rap[nvoice][0] = 1.0; //if the unison is not used, always make the only subvoice to have the default note break; case 2: { //unison for 2 subvoices unison_base_freq_rap[nvoice][0] = 1.0 / unison_real_spread; unison_base_freq_rap[nvoice][1] = unison_real_spread; }; break; default: { //unison for more than 2 subvoices REALTYPE unison_values[unison]; REALTYPE min = -1e-6, max = 1e-6; for(int k = 0; k < unison; k++) { REALTYPE step = (k / (REALTYPE) (unison - 1)) * 2.0 - 1.0; //this makes the unison spread more uniform REALTYPE val = step + (RND * 2.0 - 1.0) / (unison - 1); unison_values[k] = val; if(val > max) max = val; if(val < min) min = val; } REALTYPE diff = max - min; for(int k = 0; k < unison; k++) { unison_values[k] = (unison_values[k] - (max + min) * 0.5) / diff; //the lowest value will be -1 and the highest will be 1 unison_base_freq_rap[nvoice][k] = pow(2.0, (unison_spread * unison_values[k]) / 1200); } }; } //unison vibrattos if(unison > 1) { for(int k = 0; k < unison; k++) //reduce the frequency difference for larger vibrattos unison_base_freq_rap[nvoice][k] = 1.0 + (unison_base_freq_rap[ nvoice][k] - 1.0) * (1.0 - unison_vibratto_a); ; } unison_vibratto[nvoice].step = new REALTYPE[unison]; unison_vibratto[nvoice].position = new REALTYPE[unison]; unison_vibratto[nvoice].amplitude = (unison_real_spread - 1.0) * unison_vibratto_a; REALTYPE increments_per_second = SAMPLE_RATE / (REALTYPE)SOUND_BUFFER_SIZE; REALTYPE vibratto_base_period = 0.25 * pow(2.0, (1.0 - pars->VoicePar[nvoice]. Unison_vibratto_speed / 127.0) * 4.0); for(int k = 0; k < unison; k++) { unison_vibratto[nvoice].position[k] = RND * 1.8 - 0.9; REALTYPE vibratto_period = vibratto_base_period * pow( 2.0, RND * 2.0 - 1.0); //make period to vary randomly from 50% to 200% vibratto base period REALTYPE m = 4.0 / (vibratto_period * increments_per_second); if(RND < 0.5) m = -m; unison_vibratto[nvoice].step[k] = m; } if(unison == 1) { //no vibratto for a single voice unison_vibratto[nvoice].step[0] = 0.0; unison_vibratto[nvoice].position[0] = 0.0; unison_vibratto[nvoice].amplitude = 0.0; } //phase invert for unison unison_invert_phase[nvoice][0] = false; if(unison != 1) { int inv = pars->VoicePar[nvoice].Unison_invert_phase; switch(inv) { case 0: for(int k = 0; k < unison; k++) unison_invert_phase[nvoice][k] = false; break; case 1: for(int k = 0; k < unison; k++) unison_invert_phase[nvoice][k] = (RND > 0.5); break; default: for(int k = 0; k < unison; k++) unison_invert_phase[nvoice][k] = (k % inv == 0) ? true : false; break; } } oscfreqhi[nvoice] = new int[unison]; oscfreqlo[nvoice] = new REALTYPE[unison]; oscfreqhiFM[nvoice] = new unsigned int[unison]; oscfreqloFM[nvoice] = new REALTYPE[unison]; oscposhi[nvoice] = new int[unison]; oscposlo[nvoice] = new REALTYPE[unison]; oscposhiFM[nvoice] = new unsigned int[unison]; oscposloFM[nvoice] = new REALTYPE[unison]; NoteVoicePar[nvoice].Enabled = ON; NoteVoicePar[nvoice].fixedfreq = pars->VoicePar[nvoice].Pfixedfreq; NoteVoicePar[nvoice].fixedfreqET = pars->VoicePar[nvoice].PfixedfreqET; //use the Globalpars.detunetype if the detunetype is 0 if(pars->VoicePar[nvoice].PDetuneType != 0) { NoteVoicePar[nvoice].Detune = getdetune( pars->VoicePar[nvoice].PDetuneType, pars->VoicePar[nvoice]. PCoarseDetune, 8192); //coarse detune NoteVoicePar[nvoice].FineDetune = getdetune( pars->VoicePar[nvoice].PDetuneType, 0, pars->VoicePar[nvoice].PDetune); //fine detune } else { NoteVoicePar[nvoice].Detune = getdetune( pars->GlobalPar.PDetuneType, pars->VoicePar[nvoice]. PCoarseDetune, 8192); //coarse detune NoteVoicePar[nvoice].FineDetune = getdetune( pars->GlobalPar.PDetuneType, 0, pars->VoicePar[nvoice].PDetune); //fine detune } if(pars->VoicePar[nvoice].PFMDetuneType != 0) NoteVoicePar[nvoice].FMDetune = getdetune( pars->VoicePar[nvoice].PFMDetuneType, pars->VoicePar[nvoice]. PFMCoarseDetune, pars->VoicePar[nvoice].PFMDetune); else NoteVoicePar[nvoice].FMDetune = getdetune( pars->GlobalPar.PDetuneType, pars->VoicePar[nvoice]. PFMCoarseDetune, pars->VoicePar[nvoice].PFMDetune); ; for(int k = 0; k < unison; k++) { oscposhi[nvoice][k] = 0; oscposlo[nvoice][k] = 0.0; oscposhiFM[nvoice][k] = 0; oscposloFM[nvoice][k] = 0.0; } NoteVoicePar[nvoice].OscilSmp = new REALTYPE[OSCIL_SIZE + OSCIL_SMP_EXTRA_SAMPLES]; //the extra points contains the first point //Get the voice's oscil or external's voice oscil int vc = nvoice; if(pars->VoicePar[nvoice].Pextoscil != -1) vc = pars->VoicePar[nvoice].Pextoscil; if(!pars->GlobalPar.Hrandgrouping) pars->VoicePar[vc].OscilSmp->newrandseed(rand()); int oscposhi_start = pars->VoicePar[vc].OscilSmp->get(NoteVoicePar[nvoice].OscilSmp, getvoicebasefreq(nvoice), pars->VoicePar[nvoice].Presonance); //I store the first elments to the last position for speedups for(int i = 0; i < OSCIL_SMP_EXTRA_SAMPLES; i++) NoteVoicePar[nvoice].OscilSmp[OSCIL_SIZE + i] = NoteVoicePar[nvoice].OscilSmp[i]; oscposhi_start += (int)((pars->VoicePar[nvoice].Poscilphase - 64.0) / 128.0 * OSCIL_SIZE + OSCIL_SIZE * 4); oscposhi_start %= OSCIL_SIZE; for(int k = 0; k < unison; k++) { oscposhi[nvoice][k] = oscposhi_start; oscposhi_start = (int)(RND * (OSCIL_SIZE - 1)); //put random starting point for other subvoices } NoteVoicePar[nvoice].FreqLfo = NULL; NoteVoicePar[nvoice].FreqEnvelope = NULL; NoteVoicePar[nvoice].AmpLfo = NULL; NoteVoicePar[nvoice].AmpEnvelope = NULL; NoteVoicePar[nvoice].VoiceFilterL = NULL; NoteVoicePar[nvoice].VoiceFilterR = NULL; NoteVoicePar[nvoice].FilterEnvelope = NULL; NoteVoicePar[nvoice].FilterLfo = NULL; NoteVoicePar[nvoice].FilterCenterPitch = pars->VoicePar[nvoice].VoiceFilter->getfreq(); NoteVoicePar[nvoice].filterbypass = pars->VoicePar[nvoice].Pfilterbypass; switch(pars->VoicePar[nvoice].PFMEnabled) { case 1: NoteVoicePar[nvoice].FMEnabled = MORPH; break; case 2: NoteVoicePar[nvoice].FMEnabled = RING_MOD; break; case 3: NoteVoicePar[nvoice].FMEnabled = PHASE_MOD; break; case 4: NoteVoicePar[nvoice].FMEnabled = FREQ_MOD; break; case 5: NoteVoicePar[nvoice].FMEnabled = PITCH_MOD; break; default: NoteVoicePar[nvoice].FMEnabled = NONE; } NoteVoicePar[nvoice].FMVoice = pars->VoicePar[nvoice].PFMVoice; NoteVoicePar[nvoice].FMFreqEnvelope = NULL; NoteVoicePar[nvoice].FMAmpEnvelope = NULL; //Compute the Voice's modulator volume (incl. damping) REALTYPE fmvoldamp = pow(440.0 / getvoicebasefreq( nvoice), pars->VoicePar[nvoice].PFMVolumeDamp / 64.0 - 1.0); switch(NoteVoicePar[nvoice].FMEnabled) { case PHASE_MOD: fmvoldamp = pow(440.0 / getvoicebasefreq( nvoice), pars->VoicePar[nvoice].PFMVolumeDamp / 64.0); NoteVoicePar[nvoice].FMVolume = (exp(pars->VoicePar[nvoice].PFMVolume / 127.0 * FM_AMP_MULTIPLIER) - 1.0) * fmvoldamp * 4.0; break; case FREQ_MOD: NoteVoicePar[nvoice].FMVolume = (exp(pars->VoicePar[nvoice].PFMVolume / 127.0 * FM_AMP_MULTIPLIER) - 1.0) * fmvoldamp * 4.0; break; // case PITCH_MOD:NoteVoicePar[nvoice].FMVolume=(pars->VoicePar[nvoice].PFMVolume/127.0*8.0)*fmvoldamp;//??????????? // break; default: if(fmvoldamp > 1.0) fmvoldamp = 1.0; NoteVoicePar[nvoice].FMVolume = pars->VoicePar[nvoice].PFMVolume / 127.0 * fmvoldamp; } //Voice's modulator velocity sensing NoteVoicePar[nvoice].FMVolume *= VelF(velocity, partparams->VoicePar[nvoice].PFMVelocityScaleFunction); FMoldsmp[nvoice] = new REALTYPE [unison]; for(int k = 0; k < unison; k++) FMoldsmp[nvoice][k] = 0.0; //this is for FM (integration) firsttick[nvoice] = 1; NoteVoicePar[nvoice].DelayTicks = (int)((exp(pars->VoicePar[nvoice].PDelay / 127.0 * log(50.0)) - 1.0) / SOUND_BUFFER_SIZE / 10.0 * SAMPLE_RATE); } max_unison = 1; for(int nvoice = 0; nvoice < NUM_VOICES; nvoice++) if(unison_size[nvoice] > max_unison) max_unison = unison_size[nvoice]; ; tmpwave_unison = new REALTYPE *[max_unison]; for(int k = 0; k < max_unison; k++) { tmpwave_unison[k] = new REALTYPE[SOUND_BUFFER_SIZE]; memset(tmpwave_unison[k], 0, SOUND_BUFFER_SIZE * sizeof(REALTYPE)); } initparameters(); ready = 1; } // ADlegatonote: This function is (mostly) a copy of ADnote(...) and // 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_, 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; 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(); if(pars->GlobalPar.PPanning == 0) NoteGlobalPar.Panning = RND; else NoteGlobalPar.Panning = pars->GlobalPar.PPanning / 128.0; NoteGlobalPar.FilterCenterPitch = pars->GlobalPar.GlobalFilter->getfreq() //center freq + pars->GlobalPar.PFilterVelocityScale / 127.0 * 6.0 //velocity sensing * (VelF(velocity, pars->GlobalPar. PFilterVelocityScaleFunction) - 1); for(int nvoice = 0; nvoice < NUM_VOICES; nvoice++) { if(NoteVoicePar[nvoice].Enabled == OFF) continue; //(gf) Stay the same as first note in legato. NoteVoicePar[nvoice].fixedfreq = pars->VoicePar[nvoice].Pfixedfreq; NoteVoicePar[nvoice].fixedfreqET = pars->VoicePar[nvoice].PfixedfreqET; //use the Globalpars.detunetype if the detunetype is 0 if(pars->VoicePar[nvoice].PDetuneType != 0) { NoteVoicePar[nvoice].Detune = getdetune( pars->VoicePar[nvoice].PDetuneType, pars->VoicePar[nvoice]. PCoarseDetune, 8192); //coarse detune NoteVoicePar[nvoice].FineDetune = getdetune( pars->VoicePar[nvoice].PDetuneType, 0, pars->VoicePar[nvoice].PDetune); //fine detune } else { NoteVoicePar[nvoice].Detune = getdetune( pars->GlobalPar.PDetuneType, pars->VoicePar[nvoice]. PCoarseDetune, 8192); //coarse detune NoteVoicePar[nvoice].FineDetune = getdetune( pars->GlobalPar.PDetuneType, 0, pars->VoicePar[nvoice].PDetune); //fine detune } if(pars->VoicePar[nvoice].PFMDetuneType != 0) NoteVoicePar[nvoice].FMDetune = getdetune( pars->VoicePar[nvoice].PFMDetuneType, pars->VoicePar[nvoice]. PFMCoarseDetune, pars->VoicePar[nvoice].PFMDetune); else NoteVoicePar[nvoice].FMDetune = getdetune( pars->GlobalPar.PDetuneType, pars->VoicePar[nvoice]. PFMCoarseDetune, pars->VoicePar[nvoice].PFMDetune); ; //Get the voice's oscil or external's voice oscil int vc = nvoice; if(pars->VoicePar[nvoice].Pextoscil != -1) vc = pars->VoicePar[nvoice].Pextoscil; if(!pars->GlobalPar.Hrandgrouping) pars->VoicePar[vc].OscilSmp->newrandseed(rand()); pars->VoicePar[vc].OscilSmp->get(NoteVoicePar[nvoice].OscilSmp, getvoicebasefreq(nvoice), pars->VoicePar[nvoice].Presonance); //(gf)Modif of the above line. //I store the first elments to the last position for speedups for(int i = 0; i < OSCIL_SMP_EXTRA_SAMPLES; i++) NoteVoicePar[nvoice].OscilSmp[OSCIL_SIZE + i] = NoteVoicePar[nvoice].OscilSmp[i]; NoteVoicePar[nvoice].FilterCenterPitch = pars->VoicePar[nvoice].VoiceFilter->getfreq(); NoteVoicePar[nvoice].filterbypass = pars->VoicePar[nvoice].Pfilterbypass; NoteVoicePar[nvoice].FMVoice = pars->VoicePar[nvoice].PFMVoice; //Compute the Voice's modulator volume (incl. damping) REALTYPE fmvoldamp = pow(440.0 / getvoicebasefreq( nvoice), pars->VoicePar[nvoice].PFMVolumeDamp / 64.0 - 1.0); switch(NoteVoicePar[nvoice].FMEnabled) { case PHASE_MOD: fmvoldamp = pow(440.0 / getvoicebasefreq( nvoice), pars->VoicePar[nvoice].PFMVolumeDamp / 64.0); NoteVoicePar[nvoice].FMVolume = (exp(pars->VoicePar[nvoice].PFMVolume / 127.0 * FM_AMP_MULTIPLIER) - 1.0) * fmvoldamp * 4.0; break; case FREQ_MOD: NoteVoicePar[nvoice].FMVolume = (exp(pars->VoicePar[nvoice].PFMVolume / 127.0 * FM_AMP_MULTIPLIER) - 1.0) * fmvoldamp * 4.0; break; // case PITCH_MOD:NoteVoicePar[nvoice].FMVolume=(pars->VoicePar[nvoice].PFMVolume/127.0*8.0)*fmvoldamp;//??????????? // break; default: if(fmvoldamp > 1.0) fmvoldamp = 1.0; NoteVoicePar[nvoice].FMVolume = pars->VoicePar[nvoice].PFMVolume / 127.0 * fmvoldamp; } //Voice's modulator velocity sensing NoteVoicePar[nvoice].FMVolume *= VelF(velocity, partparams->VoicePar[nvoice].PFMVelocityScaleFunction); NoteVoicePar[nvoice].DelayTicks = (int)((exp(pars->VoicePar[nvoice].PDelay / 127.0 * log(50.0)) - 1.0) / SOUND_BUFFER_SIZE / 10.0 * SAMPLE_RATE); } /// initparameters(); /////////////// // Altered content of initparameters(): int nvoice, i, tmp[NUM_VOICES]; NoteGlobalPar.Volume = 4.0 * pow(0.1, 3.0 * (1.0 - partparams->GlobalPar.PVolume / 96.0)) //-60 dB .. 0 dB * VelF( velocity, partparams->GlobalPar. PAmpVelocityScaleFunction); //velocity sensing globalnewamplitude = NoteGlobalPar.Volume * NoteGlobalPar.AmpEnvelope->envout_dB() * NoteGlobalPar.AmpLfo->amplfoout(); NoteGlobalPar.FilterQ = partparams->GlobalPar.GlobalFilter->getq(); NoteGlobalPar.FilterFreqTracking = partparams->GlobalPar.GlobalFilter->getfreqtracking(basefreq); // Forbids the Modulation Voice to be greater or equal than voice for(i = 0; i < NUM_VOICES; i++) if(NoteVoicePar[i].FMVoice >= i) NoteVoicePar[i].FMVoice = -1; // Voice Parameter init for(nvoice = 0; nvoice < NUM_VOICES; nvoice++) { if(NoteVoicePar[nvoice].Enabled == 0) continue; NoteVoicePar[nvoice].noisetype = partparams->VoicePar[nvoice].Type; /* Voice Amplitude Parameters Init */ NoteVoicePar[nvoice].Volume = pow(0.1, 3.0 * (1.0 - partparams->VoicePar[nvoice].PVolume / 127.0)) // -60 dB .. 0 dB * VelF(velocity, partparams->VoicePar[nvoice].PAmpVelocityScaleFunction); //velocity if(partparams->VoicePar[nvoice].PVolumeminus != 0) NoteVoicePar[nvoice].Volume = -NoteVoicePar[nvoice].Volume; if(partparams->VoicePar[nvoice].PPanning == 0) NoteVoicePar[nvoice].Panning = RND; // random panning else NoteVoicePar[nvoice].Panning = partparams->VoicePar[nvoice].PPanning / 128.0; newamplitude[nvoice] = 1.0; if((partparams->VoicePar[nvoice].PAmpEnvelopeEnabled != 0) && (NoteVoicePar[nvoice].AmpEnvelope != NULL)) newamplitude[nvoice] *= NoteVoicePar[nvoice].AmpEnvelope->envout_dB(); ; if((partparams->VoicePar[nvoice].PAmpLfoEnabled != 0) && (NoteVoicePar[nvoice].AmpLfo != NULL)) newamplitude[nvoice] *= NoteVoicePar[nvoice].AmpLfo->amplfoout(); ; NoteVoicePar[nvoice].FilterFreqTracking = partparams->VoicePar[nvoice].VoiceFilter->getfreqtracking(basefreq); /* Voice Modulation Parameters Init */ if((NoteVoicePar[nvoice].FMEnabled != NONE) && (NoteVoicePar[nvoice].FMVoice < 0)) { partparams->VoicePar[nvoice].FMSmp->newrandseed(rand()); //Perform Anti-aliasing only on MORPH or RING MODULATION int vc = nvoice; if(partparams->VoicePar[nvoice].PextFMoscil != -1) vc = partparams->VoicePar[nvoice].PextFMoscil; REALTYPE tmp = 1.0; if((partparams->VoicePar[vc].FMSmp->Padaptiveharmonics != 0) || (NoteVoicePar[nvoice].FMEnabled == MORPH) || (NoteVoicePar[nvoice].FMEnabled == RING_MOD)) tmp = getFMvoicebasefreq(nvoice); ; if(!partparams->GlobalPar.Hrandgrouping) partparams->VoicePar[vc].FMSmp->newrandseed(rand()); ///oscposhiFM[nvoice]=(oscposhi[nvoice]+partparams->VoicePar[vc].FMSmp->get(NoteVoicePar[nvoice].FMSmp,tmp)) % OSCIL_SIZE; // / oscposhi[nvoice]+partparams->VoicePar[vc].FMSmp->get(NoteVoicePar[nvoice].FMSmp,tmp); //(gf) Modif of the above line. for(int i = 0; i < OSCIL_SMP_EXTRA_SAMPLES; i++) NoteVoicePar[nvoice].FMSmp[OSCIL_SIZE + i] = NoteVoicePar[nvoice].FMSmp[i]; ///oscposhiFM[nvoice]+=(int)((partparams->VoicePar[nvoice].PFMoscilphase-64.0)/128.0*OSCIL_SIZE+OSCIL_SIZE*4); ///oscposhiFM[nvoice]%=OSCIL_SIZE; } FMnewamplitude[nvoice] = NoteVoicePar[nvoice].FMVolume * ctl->fmamp.relamp; if((partparams->VoicePar[nvoice].PFMAmpEnvelopeEnabled != 0) && (NoteVoicePar[nvoice].FMAmpEnvelope != NULL)) FMnewamplitude[nvoice] *= NoteVoicePar[nvoice].FMAmpEnvelope->envout_dB(); ; } for(nvoice = 0; nvoice < NUM_VOICES; nvoice++) { for(i = nvoice + 1; i < NUM_VOICES; i++) tmp[i] = 0; for(i = nvoice + 1; i < NUM_VOICES; i++) if((NoteVoicePar[i].FMVoice == nvoice) && (tmp[i] == 0)) tmp[i] = 1; ; } /////////////// // End of the ADlegatonote function. } /* * Kill a voice of ADnote */ void ADnote::KillVoice(int nvoice) { delete [] oscfreqhi[nvoice]; delete [] oscfreqlo[nvoice]; delete [] oscfreqhiFM[nvoice]; delete [] oscfreqloFM[nvoice]; delete [] oscposhi[nvoice]; delete [] oscposlo[nvoice]; delete [] oscposhiFM[nvoice]; delete [] oscposloFM[nvoice]; delete [] NoteVoicePar[nvoice].OscilSmp; delete [] unison_base_freq_rap[nvoice]; delete [] unison_freq_rap[nvoice]; delete [] unison_invert_phase[nvoice]; delete [] FMoldsmp[nvoice]; delete [] unison_vibratto[nvoice].step; delete [] unison_vibratto[nvoice].position; if(NoteVoicePar[nvoice].FreqEnvelope != NULL) delete (NoteVoicePar[nvoice].FreqEnvelope); NoteVoicePar[nvoice].FreqEnvelope = NULL; if(NoteVoicePar[nvoice].FreqLfo != NULL) delete (NoteVoicePar[nvoice].FreqLfo); NoteVoicePar[nvoice].FreqLfo = NULL; if(NoteVoicePar[nvoice].AmpEnvelope != NULL) delete (NoteVoicePar[nvoice].AmpEnvelope); NoteVoicePar[nvoice].AmpEnvelope = NULL; if(NoteVoicePar[nvoice].AmpLfo != NULL) delete (NoteVoicePar[nvoice].AmpLfo); NoteVoicePar[nvoice].AmpLfo = NULL; if(NoteVoicePar[nvoice].VoiceFilterL != NULL) delete (NoteVoicePar[nvoice].VoiceFilterL); NoteVoicePar[nvoice].VoiceFilterL = NULL; if(NoteVoicePar[nvoice].VoiceFilterR != NULL) delete (NoteVoicePar[nvoice].VoiceFilterR); NoteVoicePar[nvoice].VoiceFilterR = NULL; if(NoteVoicePar[nvoice].FilterEnvelope != NULL) delete (NoteVoicePar[nvoice].FilterEnvelope); NoteVoicePar[nvoice].FilterEnvelope = NULL; if(NoteVoicePar[nvoice].FilterLfo != NULL) delete (NoteVoicePar[nvoice].FilterLfo); NoteVoicePar[nvoice].FilterLfo = NULL; if(NoteVoicePar[nvoice].FMFreqEnvelope != NULL) delete (NoteVoicePar[nvoice].FMFreqEnvelope); NoteVoicePar[nvoice].FMFreqEnvelope = NULL; if(NoteVoicePar[nvoice].FMAmpEnvelope != NULL) delete (NoteVoicePar[nvoice].FMAmpEnvelope); NoteVoicePar[nvoice].FMAmpEnvelope = NULL; if((NoteVoicePar[nvoice].FMEnabled != NONE) && (NoteVoicePar[nvoice].FMVoice < 0)) delete [] NoteVoicePar[nvoice].FMSmp; if(NoteVoicePar[nvoice].VoiceOut != NULL) memset(NoteVoicePar[nvoice].VoiceOut, 0, SOUND_BUFFER_SIZE * sizeof(REALTYPE));//do not delete, yet: perhaps is used by another voice NoteVoicePar[nvoice].Enabled = OFF; } /* * Kill the note */ void ADnote::KillNote() { int nvoice; for(nvoice = 0; nvoice < NUM_VOICES; nvoice++) { if(NoteVoicePar[nvoice].Enabled == ON) KillVoice(nvoice); //delete VoiceOut if(NoteVoicePar[nvoice].VoiceOut != NULL) delete (NoteVoicePar[nvoice].VoiceOut); NoteVoicePar[nvoice].VoiceOut = NULL; } delete (NoteGlobalPar.FreqEnvelope); delete (NoteGlobalPar.FreqLfo); delete (NoteGlobalPar.AmpEnvelope); delete (NoteGlobalPar.AmpLfo); delete (NoteGlobalPar.GlobalFilterL); if(stereo != 0) delete (NoteGlobalPar.GlobalFilterR); delete (NoteGlobalPar.FilterEnvelope); delete (NoteGlobalPar.FilterLfo); NoteEnabled = OFF; } ADnote::~ADnote() { if(NoteEnabled == ON) KillNote(); delete [] tmpwavel; delete [] tmpwaver; delete [] bypassl; delete [] bypassr; for(int k = 0; k < max_unison; k++) delete[] tmpwave_unison[k]; delete[] tmpwave_unison; } /* * Init the parameters */ void ADnote::initparameters() { int nvoice, i, tmp[NUM_VOICES]; // Global Parameters NoteGlobalPar.FreqEnvelope = new Envelope( partparams->GlobalPar.FreqEnvelope, basefreq); NoteGlobalPar.FreqLfo = new LFO(partparams->GlobalPar.FreqLfo, basefreq); NoteGlobalPar.AmpEnvelope = new Envelope(partparams->GlobalPar.AmpEnvelope, basefreq); NoteGlobalPar.AmpLfo = new LFO(partparams->GlobalPar.AmpLfo, basefreq); NoteGlobalPar.Volume = 4.0 * pow(0.1, 3.0 * (1.0 - partparams->GlobalPar.PVolume / 96.0)) //-60 dB .. 0 dB * VelF( velocity, partparams->GlobalPar. PAmpVelocityScaleFunction); //velocity sensing NoteGlobalPar.AmpEnvelope->envout_dB(); //discard the first envelope output globalnewamplitude = NoteGlobalPar.Volume * NoteGlobalPar.AmpEnvelope->envout_dB() * NoteGlobalPar.AmpLfo->amplfoout(); NoteGlobalPar.GlobalFilterL = new Filter(partparams->GlobalPar.GlobalFilter); if(stereo != 0) NoteGlobalPar.GlobalFilterR = new Filter( partparams->GlobalPar.GlobalFilter); NoteGlobalPar.FilterEnvelope = new Envelope( partparams->GlobalPar.FilterEnvelope, basefreq); NoteGlobalPar.FilterLfo = new LFO(partparams->GlobalPar.FilterLfo, basefreq); NoteGlobalPar.FilterQ = partparams->GlobalPar.GlobalFilter->getq(); NoteGlobalPar.FilterFreqTracking = partparams->GlobalPar.GlobalFilter->getfreqtracking(basefreq); // Forbids the Modulation Voice to be greater or equal than voice for(i = 0; i < NUM_VOICES; i++) if(NoteVoicePar[i].FMVoice >= i) NoteVoicePar[i].FMVoice = -1; // Voice Parameter init for(nvoice = 0; nvoice < NUM_VOICES; nvoice++) { if(NoteVoicePar[nvoice].Enabled == 0) continue; NoteVoicePar[nvoice].noisetype = partparams->VoicePar[nvoice].Type; /* Voice Amplitude Parameters Init */ NoteVoicePar[nvoice].Volume = pow(0.1, 3.0 * (1.0 - partparams->VoicePar[nvoice].PVolume / 127.0)) // -60 dB .. 0 dB * VelF(velocity, partparams->VoicePar[nvoice].PAmpVelocityScaleFunction); //velocity if(partparams->VoicePar[nvoice].PVolumeminus != 0) NoteVoicePar[nvoice].Volume = -NoteVoicePar[nvoice].Volume; if(partparams->VoicePar[nvoice].PPanning == 0) NoteVoicePar[nvoice].Panning = RND; // random panning else NoteVoicePar[nvoice].Panning = partparams->VoicePar[nvoice].PPanning / 128.0; newamplitude[nvoice] = 1.0; if(partparams->VoicePar[nvoice].PAmpEnvelopeEnabled != 0) { NoteVoicePar[nvoice].AmpEnvelope = new Envelope( partparams->VoicePar[nvoice].AmpEnvelope, basefreq); NoteVoicePar[nvoice].AmpEnvelope->envout_dB(); //discard the first envelope sample newamplitude[nvoice] *= NoteVoicePar[nvoice].AmpEnvelope->envout_dB(); } if(partparams->VoicePar[nvoice].PAmpLfoEnabled != 0) { NoteVoicePar[nvoice].AmpLfo = new LFO( partparams->VoicePar[nvoice].AmpLfo, basefreq); newamplitude[nvoice] *= NoteVoicePar[nvoice].AmpLfo->amplfoout(); } /* Voice Frequency Parameters Init */ if(partparams->VoicePar[nvoice].PFreqEnvelopeEnabled != 0) NoteVoicePar[nvoice].FreqEnvelope = new Envelope( partparams->VoicePar[nvoice].FreqEnvelope, basefreq); if(partparams->VoicePar[nvoice].PFreqLfoEnabled != 0) NoteVoicePar[nvoice].FreqLfo = new LFO( partparams->VoicePar[nvoice].FreqLfo, basefreq); /* Voice Filter Parameters Init */ if(partparams->VoicePar[nvoice].PFilterEnabled != 0) { NoteVoicePar[nvoice].VoiceFilterL = new Filter( partparams->VoicePar[nvoice].VoiceFilter); NoteVoicePar[nvoice].VoiceFilterR = new Filter( partparams->VoicePar[nvoice].VoiceFilter); } if(partparams->VoicePar[nvoice].PFilterEnvelopeEnabled != 0) NoteVoicePar[nvoice].FilterEnvelope = new Envelope( partparams->VoicePar[nvoice].FilterEnvelope, basefreq); if(partparams->VoicePar[nvoice].PFilterLfoEnabled != 0) NoteVoicePar[nvoice].FilterLfo = new LFO(partparams->VoicePar[nvoice].FilterLfo, basefreq); NoteVoicePar[nvoice].FilterFreqTracking = partparams->VoicePar[nvoice].VoiceFilter->getfreqtracking(basefreq); /* Voice Modulation Parameters Init */ if((NoteVoicePar[nvoice].FMEnabled != NONE) && (NoteVoicePar[nvoice].FMVoice < 0)) { partparams->VoicePar[nvoice].FMSmp->newrandseed(rand()); NoteVoicePar[nvoice].FMSmp = new REALTYPE[OSCIL_SIZE + OSCIL_SMP_EXTRA_SAMPLES]; //Perform Anti-aliasing only on MORPH or RING MODULATION int vc = nvoice; if(partparams->VoicePar[nvoice].PextFMoscil != -1) vc = partparams->VoicePar[nvoice].PextFMoscil; REALTYPE tmp = 1.0; if((partparams->VoicePar[vc].FMSmp->Padaptiveharmonics != 0) || (NoteVoicePar[nvoice].FMEnabled == MORPH) || (NoteVoicePar[nvoice].FMEnabled == RING_MOD)) tmp = getFMvoicebasefreq(nvoice); ; if(!partparams->GlobalPar.Hrandgrouping) partparams->VoicePar[vc].FMSmp->newrandseed(rand()); for(int k = 0; k < unison_size[nvoice]; k++) oscposhiFM[nvoice][k] = (oscposhi[nvoice][k] + partparams->VoicePar[vc].FMSmp->get(NoteVoicePar[nvoice] . FMSmp, tmp)) % OSCIL_SIZE; ; for(int i = 0; i < OSCIL_SMP_EXTRA_SAMPLES; i++) NoteVoicePar[nvoice].FMSmp[OSCIL_SIZE + i] = NoteVoicePar[nvoice].FMSmp[i]; int oscposhiFM_add = (int)((partparams->VoicePar[nvoice].PFMoscilphase - 64.0) / 128.0 * OSCIL_SIZE + OSCIL_SIZE * 4); for(int k = 0; k < unison_size[nvoice]; k++) { oscposhiFM[nvoice][k] += oscposhiFM_add; oscposhiFM[nvoice][k] %= OSCIL_SIZE; } } if(partparams->VoicePar[nvoice].PFMFreqEnvelopeEnabled != 0) NoteVoicePar[nvoice].FMFreqEnvelope = new Envelope( partparams->VoicePar[nvoice].FMFreqEnvelope, basefreq); FMnewamplitude[nvoice] = NoteVoicePar[nvoice].FMVolume * ctl->fmamp.relamp; if(partparams->VoicePar[nvoice].PFMAmpEnvelopeEnabled != 0) { NoteVoicePar[nvoice].FMAmpEnvelope = new Envelope( partparams->VoicePar[nvoice].FMAmpEnvelope, basefreq); FMnewamplitude[nvoice] *= NoteVoicePar[nvoice].FMAmpEnvelope->envout_dB(); } } for(nvoice = 0; nvoice < NUM_VOICES; nvoice++) { for(i = nvoice + 1; i < NUM_VOICES; i++) tmp[i] = 0; for(i = nvoice + 1; i < NUM_VOICES; i++) if((NoteVoicePar[i].FMVoice == nvoice) && (tmp[i] == 0)) { NoteVoicePar[nvoice].VoiceOut = new REALTYPE[SOUND_BUFFER_SIZE]; tmp[i] = 1; } ; if(NoteVoicePar[nvoice].VoiceOut != NULL) memset(NoteVoicePar[nvoice].VoiceOut, 0, SOUND_BUFFER_SIZE * sizeof(REALTYPE)); } } /* * Computes the relative frequency of each unison voice and it's vibratto * This must be called before setfreq* functions */ void ADnote::compute_unison_freq_rap(int nvoice) { if(unison_size[nvoice] == 1) { //no unison unison_freq_rap[nvoice][0] = 1.0; return; } REALTYPE relbw = ctl->bandwidth.relbw * bandwidthDetuneMultiplier; for(int k = 0; k < unison_size[nvoice]; k++) { REALTYPE pos = unison_vibratto[nvoice].position[k]; REALTYPE step = unison_vibratto[nvoice].step[k]; pos += step; if(pos <= -1.0) { pos = -1.0; step = -step; } if(pos >= 1.0) { pos = 1.0; step = -step; } REALTYPE vibratto_val = (pos - 0.333333333 * pos * pos * pos) * 1.5; //make the vibratto lfo smoother unison_freq_rap[nvoice][k] = 1.0 + ((unison_base_freq_rap[nvoice][k] - 1.0) + vibratto_val * unison_vibratto[nvoice].amplitude) * relbw; unison_vibratto[nvoice].position[k] = pos; step = unison_vibratto[nvoice].step[k] = step; } } /* * Computes the frequency of an oscillator */ void ADnote::setfreq(int nvoice, REALTYPE in_freq) { for(int k = 0; k < unison_size[nvoice]; k++) { REALTYPE freq = fabs(in_freq) * unison_freq_rap[nvoice][k]; REALTYPE speed = freq * REALTYPE(OSCIL_SIZE) / (REALTYPE) SAMPLE_RATE; if(speed > OSCIL_SIZE) speed = OSCIL_SIZE; F2I(speed, oscfreqhi[nvoice][k]); oscfreqlo[nvoice][k] = speed - floor(speed); } } /* * Computes the frequency of an modullator oscillator */ void ADnote::setfreqFM(int nvoice, REALTYPE in_freq) { for(int k = 0; k < unison_size[nvoice]; k++) { REALTYPE freq = fabs(in_freq) * unison_freq_rap[nvoice][k]; REALTYPE speed = freq * REALTYPE(OSCIL_SIZE) / (REALTYPE) SAMPLE_RATE; if(speed > OSCIL_SIZE) speed = OSCIL_SIZE; F2I(speed, oscfreqhiFM[nvoice][k]); oscfreqloFM[nvoice][k] = speed - floor(speed); } } /* * Get Voice base frequency */ REALTYPE ADnote::getvoicebasefreq(int nvoice) const { REALTYPE detune = NoteVoicePar[nvoice].Detune / 100.0 + NoteVoicePar[nvoice].FineDetune / 100.0 * ctl->bandwidth.relbw * bandwidthDetuneMultiplier + NoteGlobalPar.Detune / 100.0; if(NoteVoicePar[nvoice].fixedfreq == 0) return this->basefreq * pow(2, detune / 12.0); else { //the fixed freq is enabled REALTYPE fixedfreq = 440.0; int fixedfreqET = NoteVoicePar[nvoice].fixedfreqET; if(fixedfreqET != 0) { //if the frequency varies according the keyboard note REALTYPE tmp = (midinote - 69.0) / 12.0 * (pow(2.0, (fixedfreqET - 1) / 63.0) - 1.0); if(fixedfreqET <= 64) fixedfreq *= pow(2.0, tmp); else fixedfreq *= pow(3.0, tmp); } return fixedfreq * pow(2.0, detune / 12.0); } } /* * Get Voice's Modullator base frequency */ REALTYPE ADnote::getFMvoicebasefreq(int nvoice) const { REALTYPE detune = NoteVoicePar[nvoice].FMDetune / 100.0; return getvoicebasefreq(nvoice) * pow(2, detune / 12.0); } /* * Computes all the parameters for each tick */ void ADnote::computecurrentparameters() { int nvoice; REALTYPE voicefreq, voicepitch, filterpitch, filterfreq, FMfreq, FMrelativepitch, globalpitch, globalfilterpitch; globalpitch = 0.01 * (NoteGlobalPar.FreqEnvelope->envout() + NoteGlobalPar.FreqLfo->lfoout() * ctl->modwheel.relmod); globaloldamplitude = globalnewamplitude; globalnewamplitude = NoteGlobalPar.Volume * NoteGlobalPar.AmpEnvelope->envout_dB() * NoteGlobalPar.AmpLfo->amplfoout(); globalfilterpitch = NoteGlobalPar.FilterEnvelope->envout() + NoteGlobalPar.FilterLfo->lfoout() + NoteGlobalPar.FilterCenterPitch; REALTYPE tmpfilterfreq = globalfilterpitch + ctl->filtercutoff.relfreq + NoteGlobalPar.FilterFreqTracking; tmpfilterfreq = NoteGlobalPar.GlobalFilterL->getrealfreq(tmpfilterfreq); REALTYPE globalfilterq = NoteGlobalPar.FilterQ * ctl->filterq.relq; NoteGlobalPar.GlobalFilterL->setfreq_and_q(tmpfilterfreq, globalfilterq); if(stereo != 0) NoteGlobalPar.GlobalFilterR->setfreq_and_q(tmpfilterfreq, globalfilterq); //compute the portamento, if it is used by this note REALTYPE portamentofreqrap = 1.0; if(portamento != 0) { //this voice use portamento portamentofreqrap = ctl->portamento.freqrap; if(ctl->portamento.used == 0) //the portamento has finished portamento = 0; //this note is no longer "portamented" ; } //compute parameters for all voices for(nvoice = 0; nvoice < NUM_VOICES; nvoice++) { if(NoteVoicePar[nvoice].Enabled != ON) continue; NoteVoicePar[nvoice].DelayTicks -= 1; if(NoteVoicePar[nvoice].DelayTicks > 0) continue; compute_unison_freq_rap(nvoice); /*******************/ /* Voice Amplitude */ /*******************/ oldamplitude[nvoice] = newamplitude[nvoice]; newamplitude[nvoice] = 1.0; if(NoteVoicePar[nvoice].AmpEnvelope != NULL) newamplitude[nvoice] *= NoteVoicePar[nvoice].AmpEnvelope->envout_dB(); if(NoteVoicePar[nvoice].AmpLfo != NULL) newamplitude[nvoice] *= NoteVoicePar[nvoice].AmpLfo->amplfoout(); /****************/ /* Voice Filter */ /****************/ if(NoteVoicePar[nvoice].VoiceFilterL != NULL) { filterpitch = NoteVoicePar[nvoice].FilterCenterPitch; if(NoteVoicePar[nvoice].FilterEnvelope != NULL) filterpitch += NoteVoicePar[nvoice].FilterEnvelope->envout(); if(NoteVoicePar[nvoice].FilterLfo != NULL) filterpitch += NoteVoicePar[nvoice].FilterLfo->lfoout(); filterfreq = filterpitch + NoteVoicePar[nvoice].FilterFreqTracking; filterfreq = NoteVoicePar[nvoice].VoiceFilterL->getrealfreq( filterfreq); NoteVoicePar[nvoice].VoiceFilterL->setfreq(filterfreq); if(stereo && NoteVoicePar[nvoice].VoiceFilterR) NoteVoicePar[nvoice].VoiceFilterR->setfreq(filterfreq); } if(NoteVoicePar[nvoice].noisetype == 0) { //compute only if the voice isn't noise /*******************/ /* Voice Frequency */ /*******************/ voicepitch = 0.0; if(NoteVoicePar[nvoice].FreqLfo != NULL) voicepitch += NoteVoicePar[nvoice].FreqLfo->lfoout() / 100.0 * ctl->bandwidth.relbw; if(NoteVoicePar[nvoice].FreqEnvelope != NULL) voicepitch += NoteVoicePar[nvoice].FreqEnvelope->envout() / 100.0; voicefreq = getvoicebasefreq(nvoice) * pow(2, (voicepitch + globalpitch) / 12.0); //Hz frequency voicefreq *= ctl->pitchwheel.relfreq; //change the frequency by the controller setfreq(nvoice, voicefreq * portamentofreqrap); /***************/ /* Modulator */ /***************/ if(NoteVoicePar[nvoice].FMEnabled != NONE) { FMrelativepitch = NoteVoicePar[nvoice].FMDetune / 100.0; if(NoteVoicePar[nvoice].FMFreqEnvelope != NULL) FMrelativepitch += NoteVoicePar[nvoice].FMFreqEnvelope->envout() / 100; FMfreq = pow(2.0, FMrelativepitch / 12.0) * voicefreq * portamentofreqrap; setfreqFM(nvoice, FMfreq); FMoldamplitude[nvoice] = FMnewamplitude[nvoice]; FMnewamplitude[nvoice] = NoteVoicePar[nvoice].FMVolume * ctl->fmamp.relamp; if(NoteVoicePar[nvoice].FMAmpEnvelope != NULL) FMnewamplitude[nvoice] *= NoteVoicePar[nvoice].FMAmpEnvelope->envout_dB(); } } } time += (REALTYPE)SOUND_BUFFER_SIZE / (REALTYPE)SAMPLE_RATE; } /* * Fadein in a way that removes clicks but keep sound "punchy" */ inline void ADnote::fadein(REALTYPE *smps) const { int zerocrossings = 0; for(int i = 1; i < SOUND_BUFFER_SIZE; i++) if((smps[i - 1] < 0.0) && (smps[i] > 0.0)) zerocrossings++; //this is only the possitive crossings REALTYPE tmp = (SOUND_BUFFER_SIZE - 1.0) / (zerocrossings + 1) / 3.0; if(tmp < 8.0) tmp = 8.0; int n; F2I(tmp, n); //how many samples is the fade-in if(n > SOUND_BUFFER_SIZE) n = SOUND_BUFFER_SIZE; for(int i = 0; i < n; i++) { //fade-in REALTYPE tmp = 0.5 - cos((REALTYPE)i / (REALTYPE) n * PI) * 0.5; smps[i] *= tmp; } } /* * Computes the Oscillator (Without Modulation) - LinearInterpolation */ inline void ADnote::ComputeVoiceOscillator_LinearInterpolation(int nvoice) { int i, poshi; REALTYPE poslo; for(int k = 0; k < unison_size[nvoice]; k++) { poshi = oscposhi[nvoice][k]; poslo = oscposlo[nvoice][k]; int freqhi = oscfreqhi[nvoice][k]; REALTYPE freqlo = oscfreqlo[nvoice][k]; REALTYPE *smps = NoteVoicePar[nvoice].OscilSmp; REALTYPE *tw = tmpwave_unison[k]; for(i = 0; i < SOUND_BUFFER_SIZE; i++) { tw[i] = smps[poshi] * (1.0 - poslo) + smps[poshi + 1] * poslo; poslo += freqlo; if(poslo >= 1.0) { poslo -= 1.0; poshi++; } poshi += freqhi; poshi &= OSCIL_SIZE - 1; } oscposhi[nvoice][k] = poshi; oscposlo[nvoice][k] = poslo; } } /* * Computes the Oscillator (Without Modulation) - CubicInterpolation * The differences from the Linear are to little to deserve to be used. This is because I am using a large OSCIL_SIZE (>512) inline void ADnote::ComputeVoiceOscillator_CubicInterpolation(int nvoice){ int i,poshi; REALTYPE poslo; poshi=oscposhi[nvoice]; poslo=oscposlo[nvoice]; REALTYPE *smps=NoteVoicePar[nvoice].OscilSmp; REALTYPE xm1,x0,x1,x2,a,b,c; for (i=0;i=1.0) { poslo-=1.0; poshi++; }; poshi+=oscfreqhi[nvoice]; poshi&=OSCIL_SIZE-1; }; oscposhi[nvoice]=poshi; oscposlo[nvoice]=poslo; }; */ /* * Computes the Oscillator (Morphing) */ inline void ADnote::ComputeVoiceOscillatorMorph(int nvoice) { int i; REALTYPE amp; ComputeVoiceOscillator_LinearInterpolation(nvoice); if(FMnewamplitude[nvoice] > 1.0) FMnewamplitude[nvoice] = 1.0; if(FMoldamplitude[nvoice] > 1.0) FMoldamplitude[nvoice] = 1.0; if(NoteVoicePar[nvoice].FMVoice >= 0) { //if I use VoiceOut[] as modullator int FMVoice = NoteVoicePar[nvoice].FMVoice; for(int k = 0; k < unison_size[nvoice]; k++) { REALTYPE *tw = tmpwave_unison[k]; for(i = 0; i < SOUND_BUFFER_SIZE; i++) { amp = INTERPOLATE_AMPLITUDE(FMoldamplitude[nvoice], FMnewamplitude[nvoice], i, SOUND_BUFFER_SIZE); tw[i] = tw[i] * (1.0 - amp) + amp * NoteVoicePar[FMVoice].VoiceOut[i]; } } } else { for(int k = 0; k < unison_size[nvoice]; k++) { int poshiFM = oscposhiFM[nvoice][k]; REALTYPE posloFM = oscposloFM[nvoice][k]; int freqhiFM = oscfreqhiFM[nvoice][k]; REALTYPE freqloFM = oscfreqloFM[nvoice][k]; REALTYPE *tw = tmpwave_unison[k]; for(i = 0; i < SOUND_BUFFER_SIZE; i++) { amp = INTERPOLATE_AMPLITUDE(FMoldamplitude[nvoice], FMnewamplitude[nvoice], i, SOUND_BUFFER_SIZE); tw[i] = tw[i] * (1.0 - amp) + amp * (NoteVoicePar[nvoice].FMSmp[poshiFM] * (1 - posloFM) + NoteVoicePar[nvoice].FMSmp[poshiFM + 1] * posloFM); posloFM += freqloFM; if(posloFM >= 1.0) { posloFM -= 1.0; poshiFM++; } poshiFM += freqhiFM; poshiFM &= OSCIL_SIZE - 1; } oscposhiFM[nvoice][k] = poshiFM; oscposloFM[nvoice][k] = posloFM; } } } /* * Computes the Oscillator (Ring Modulation) */ inline void ADnote::ComputeVoiceOscillatorRingModulation(int nvoice) { int i; REALTYPE amp; ComputeVoiceOscillator_LinearInterpolation(nvoice); if(FMnewamplitude[nvoice] > 1.0) FMnewamplitude[nvoice] = 1.0; if(FMoldamplitude[nvoice] > 1.0) FMoldamplitude[nvoice] = 1.0; if(NoteVoicePar[nvoice].FMVoice >= 0) { // if I use VoiceOut[] as modullator for(int k = 0; k < unison_size[nvoice]; k++) { REALTYPE *tw = tmpwave_unison[k]; for(i = 0; i < SOUND_BUFFER_SIZE; i++) { amp = INTERPOLATE_AMPLITUDE(FMoldamplitude[nvoice], FMnewamplitude[nvoice], i, SOUND_BUFFER_SIZE); int FMVoice = NoteVoicePar[nvoice].FMVoice; tw[i] *= (1.0 - amp) + amp * NoteVoicePar[FMVoice].VoiceOut[i]; } } } else { for(int k = 0; k < unison_size[nvoice]; k++) { int poshiFM = oscposhiFM[nvoice][k]; REALTYPE posloFM = oscposloFM[nvoice][k]; int freqhiFM = oscfreqhiFM[nvoice][k]; REALTYPE freqloFM = oscfreqloFM[nvoice][k]; REALTYPE *tw = tmpwave_unison[k]; for(i = 0; i < SOUND_BUFFER_SIZE; i++) { amp = INTERPOLATE_AMPLITUDE(FMoldamplitude[nvoice], FMnewamplitude[nvoice], i, SOUND_BUFFER_SIZE); tw[i] *= (NoteVoicePar[nvoice].FMSmp[poshiFM] * (1.0 - posloFM) + NoteVoicePar[nvoice].FMSmp[poshiFM + 1] * posloFM) * amp + (1.0 - amp); posloFM += freqloFM; if(posloFM >= 1.0) { posloFM -= 1.0; poshiFM++; } poshiFM += freqhiFM; poshiFM &= OSCIL_SIZE - 1; } oscposhiFM[nvoice][k] = poshiFM; oscposloFM[nvoice][k] = posloFM; } } } /* * Computes the Oscillator (Phase Modulation or Frequency Modulation) */ inline void ADnote::ComputeVoiceOscillatorFrequencyModulation(int nvoice, int FMmode) { int carposhi = 0; int i, FMmodfreqhi = 0; REALTYPE FMmodfreqlo = 0, carposlo = 0; if(NoteVoicePar[nvoice].FMVoice >= 0) { //if I use VoiceOut[] as modulator for(int k = 0; k < unison_size[nvoice]; k++) { REALTYPE *tw = tmpwave_unison[k]; memcpy(tw, NoteVoicePar[NoteVoicePar[nvoice].FMVoice].VoiceOut, SOUND_BUFFER_SIZE * sizeof(REALTYPE)); } } else { //Compute the modulator and store it in tmpwave_unison[][] for(int k = 0; k < unison_size[nvoice]; k++) { int poshiFM = oscposhiFM[nvoice][k]; REALTYPE posloFM = oscposloFM[nvoice][k]; int freqhiFM = oscfreqhiFM[nvoice][k]; REALTYPE freqloFM = oscfreqloFM[nvoice][k]; REALTYPE *tw = tmpwave_unison[k]; for(i = 0; i < SOUND_BUFFER_SIZE; i++) { tw[i] = (NoteVoicePar[nvoice].FMSmp[poshiFM] * (1.0 - posloFM) + NoteVoicePar[nvoice].FMSmp[poshiFM + 1] * posloFM); posloFM += freqloFM; if(posloFM >= 1.0) { posloFM = fmod(posloFM, 1.0); poshiFM++; } poshiFM += freqhiFM; poshiFM &= OSCIL_SIZE - 1; } oscposhiFM[nvoice][k] = poshiFM; oscposloFM[nvoice][k] = posloFM; } } // Amplitude interpolation if(ABOVE_AMPLITUDE_THRESHOLD(FMoldamplitude[nvoice], FMnewamplitude[nvoice])) { for(int k = 0; k < unison_size[nvoice]; k++) { REALTYPE *tw = tmpwave_unison[k]; for(i = 0; i < SOUND_BUFFER_SIZE; i++) tw[i] *= INTERPOLATE_AMPLITUDE(FMoldamplitude[nvoice], FMnewamplitude[nvoice], i, SOUND_BUFFER_SIZE); ; } } else { for(int k = 0; k < unison_size[nvoice]; k++) { REALTYPE *tw = tmpwave_unison[k]; for(i = 0; i < SOUND_BUFFER_SIZE; i++) tw[i] *= FMnewamplitude[nvoice]; } } //normalize: makes all sample-rates, oscil_sizes to produce same sound if(FMmode != 0) { //Frequency modulation REALTYPE normalize = OSCIL_SIZE / 262144.0 * 44100.0 / (REALTYPE)SAMPLE_RATE; for(int k = 0; k < unison_size[nvoice]; k++) { REALTYPE *tw = tmpwave_unison[k]; REALTYPE fmold = FMoldsmp[nvoice][k]; for(i = 0; i < SOUND_BUFFER_SIZE; i++) { fmold = fmod(fmold + tw[i] * normalize, OSCIL_SIZE); tw[i] = fmold; } FMoldsmp[nvoice][k] = fmold; } } else { //Phase modulation REALTYPE normalize = OSCIL_SIZE / 262144.0; for(int k = 0; k < unison_size[nvoice]; k++) { REALTYPE *tw = tmpwave_unison[k]; for(i = 0; i < SOUND_BUFFER_SIZE; i++) tw[i] *= normalize; } } //do the modulation for(int k = 0; k < unison_size[nvoice]; k++) { REALTYPE *tw = tmpwave_unison[k]; int poshi = oscposhi[nvoice][k]; REALTYPE poslo = oscposlo[nvoice][k]; int freqhi = oscfreqhi[nvoice][k]; REALTYPE freqlo = oscfreqlo[nvoice][k]; for(i = 0; i < SOUND_BUFFER_SIZE; i++) { F2I(tw[i], FMmodfreqhi); FMmodfreqlo = fmod(tw[i] + 0.0000000001, 1.0); if(FMmodfreqhi < 0) FMmodfreqlo++; //carrier carposhi = poshi + FMmodfreqhi; carposlo = poslo + FMmodfreqlo; if(carposlo >= 1.0) { carposhi++; carposlo = fmod(carposlo, 1.0); } carposhi &= (OSCIL_SIZE - 1); tw[i] = NoteVoicePar[nvoice].OscilSmp[carposhi] * (1.0 - carposlo) + NoteVoicePar[nvoice].OscilSmp[carposhi + 1] * carposlo; poslo += freqlo; if(poslo >= 1.0) { poslo = fmod(poslo, 1.0); poshi++; } poshi += freqhi; poshi &= OSCIL_SIZE - 1; } oscposhi[nvoice][k] = poshi; oscposlo[nvoice][k] = poslo; } } /*Calculeaza Oscilatorul cu PITCH MODULATION*/ inline void ADnote::ComputeVoiceOscillatorPitchModulation(int nvoice) { //TODO } /* * Computes the Noise */ inline void ADnote::ComputeVoiceNoise(int nvoice) { for(int k = 0; k < unison_size[nvoice]; k++) { REALTYPE *tw = tmpwave_unison[k]; for(int i = 0; i < SOUND_BUFFER_SIZE; i++) tw[i] = RND * 2.0 - 1.0; } } /* * Compute the ADnote samples * Returns 0 if the note is finished */ int ADnote::noteout(REALTYPE *outl, REALTYPE *outr) { int i, nvoice; memcpy(outl, denormalkillbuf, SOUND_BUFFER_SIZE * sizeof(REALTYPE)); memcpy(outr, denormalkillbuf, SOUND_BUFFER_SIZE * sizeof(REALTYPE)); if(NoteEnabled == OFF) return 0; memset(bypassl, 0, SOUND_BUFFER_SIZE * sizeof(REALTYPE)); memset(bypassr, 0, SOUND_BUFFER_SIZE * sizeof(REALTYPE)); computecurrentparameters(); for(nvoice = 0; nvoice < NUM_VOICES; nvoice++) { if((NoteVoicePar[nvoice].Enabled != ON) || (NoteVoicePar[nvoice].DelayTicks > 0)) continue; if(NoteVoicePar[nvoice].noisetype == 0) //voice mode=sound switch(NoteVoicePar[nvoice].FMEnabled) { case MORPH: ComputeVoiceOscillatorMorph(nvoice); break; case RING_MOD: ComputeVoiceOscillatorRingModulation(nvoice); break; case PHASE_MOD: ComputeVoiceOscillatorFrequencyModulation(nvoice, 0); break; case FREQ_MOD: ComputeVoiceOscillatorFrequencyModulation(nvoice, 1); break; //case PITCH_MOD:ComputeVoiceOscillatorPitchModulation(nvoice);break; default: ComputeVoiceOscillator_LinearInterpolation(nvoice); //if (config.cfg.Interpolation) ComputeVoiceOscillator_CubicInterpolation(nvoice); } else ComputeVoiceNoise(nvoice); // Voice Processing //mix subvoices into voice memset(tmpwavel, 0, SOUND_BUFFER_SIZE * sizeof(REALTYPE)); if(stereo) memset(tmpwaver, 0, SOUND_BUFFER_SIZE * sizeof(REALTYPE)); for(int k = 0; k < unison_size[nvoice]; k++) { REALTYPE *tw = tmpwave_unison[k]; if(stereo) { REALTYPE stereo_pos = 0; if(unison_size[nvoice] > 1) stereo_pos = k / (REALTYPE)(unison_size[nvoice] - 1) * 2.0 - 1.0; REALTYPE stereo_spread = unison_stereo_spread[nvoice] * 2.0; //between 0 and 2.0 if(stereo_spread > 1.0) { REALTYPE stereo_pos_1 = (stereo_pos >= 0.0) ? 1.0 : -1.0; stereo_pos = (2.0 - stereo_spread) * stereo_pos + (stereo_spread - 1.0) * stereo_pos_1; } else stereo_pos *= stereo_spread; ; if(unison_size[nvoice] == 1) stereo_pos = 0.0; REALTYPE panning = (stereo_pos + 1.0) * 0.5; REALTYPE lvol = (1.0 - panning) * 2.0; if(lvol > 1.0) lvol = 1.0; REALTYPE rvol = panning * 2.0; if(rvol > 1.0) rvol = 1.0; if(unison_invert_phase[nvoice][k]) { lvol = -lvol; rvol = -rvol; } for(i = 0; i < SOUND_BUFFER_SIZE; i++) tmpwavel[i] += tw[i] * lvol; for(i = 0; i < SOUND_BUFFER_SIZE; i++) tmpwaver[i] += tw[i] * rvol; } else for(i = 0; i < SOUND_BUFFER_SIZE; i++) tmpwavel[i] += tw[i]; ; } REALTYPE unison_amplitude = 1.0 / sqrt(unison_size[nvoice]); //reduce the amplitude for large unison sizes // Amplitude REALTYPE oldam = oldamplitude[nvoice] * unison_amplitude; REALTYPE newam = newamplitude[nvoice] * unison_amplitude; if(ABOVE_AMPLITUDE_THRESHOLD(oldam, newam)) { int rest = SOUND_BUFFER_SIZE; //test if the amplitude if raising and the difference is high if((newam > oldam) && ((newam - oldam) > 0.25)) { rest = 10; if(rest > SOUND_BUFFER_SIZE) rest = SOUND_BUFFER_SIZE; for(int i = 0; i < SOUND_BUFFER_SIZE - rest; i++) tmpwavel[i] *= oldam; if(stereo) for(int i = 0; i < SOUND_BUFFER_SIZE - rest; i++) tmpwaver[i] *= oldam; } // Amplitude interpolation for(i = 0; i < rest; i++) { REALTYPE amp = INTERPOLATE_AMPLITUDE(oldam, newam, i, rest); tmpwavel[i + (SOUND_BUFFER_SIZE - rest)] *= amp; if(stereo) tmpwaver[i + (SOUND_BUFFER_SIZE - rest)] *= amp; } } else { for(i = 0; i < SOUND_BUFFER_SIZE; i++) tmpwavel[i] *= newam; if(stereo) for(i = 0; i < SOUND_BUFFER_SIZE; i++) tmpwaver[i] *= newam; } // Fade in if(firsttick[nvoice] != 0) { fadein(&tmpwavel[0]); if(stereo) fadein(&tmpwaver[0]); firsttick[nvoice] = 0; } // Filter if(NoteVoicePar[nvoice].VoiceFilterL != NULL) NoteVoicePar[nvoice].VoiceFilterL->filterout(&tmpwavel[0]); if((stereo) && (NoteVoicePar[nvoice].VoiceFilterR != NULL)) NoteVoicePar[nvoice].VoiceFilterR->filterout(&tmpwaver[0]); //check if the amplitude envelope is finished, if yes, the voice will be fadeout if(NoteVoicePar[nvoice].AmpEnvelope != NULL) { if(NoteVoicePar[nvoice].AmpEnvelope->finished() != 0) { for(i = 0; i < SOUND_BUFFER_SIZE; i++) tmpwavel[i] *= 1.0 - (REALTYPE)i / (REALTYPE)SOUND_BUFFER_SIZE; if(stereo) for(i = 0; i < SOUND_BUFFER_SIZE; i++) tmpwaver[i] *= 1.0 - (REALTYPE)i / (REALTYPE)SOUND_BUFFER_SIZE; } //the voice is killed later } // Put the ADnote samples in VoiceOut (without appling Global volume, because I wish to use this voice as a modullator) if(NoteVoicePar[nvoice].VoiceOut != NULL) { if(stereo) for(i = 0; i < SOUND_BUFFER_SIZE; i++) NoteVoicePar[nvoice].VoiceOut[i] = tmpwavel[i] + tmpwaver[i]; else //mono for(i = 0; i < SOUND_BUFFER_SIZE; i++) NoteVoicePar[nvoice].VoiceOut[i] = tmpwavel[i]; ; } // Add the voice that do not bypass the filter to out if(NoteVoicePar[nvoice].filterbypass == 0) { //no bypass if(stereo) { for(i = 0; i < SOUND_BUFFER_SIZE; i++) { //stereo outl[i] += tmpwavel[i] * NoteVoicePar[nvoice].Volume * NoteVoicePar[nvoice].Panning * 2.0; outr[i] += tmpwaver[i] * NoteVoicePar[nvoice].Volume * (1.0 - NoteVoicePar[nvoice].Panning) * 2.0; } } else for(i = 0; i < SOUND_BUFFER_SIZE; i++) outl[i] += tmpwavel[i] * NoteVoicePar[nvoice].Volume; //mono ; } else { //bypass the filter if(stereo) { for(i = 0; i < SOUND_BUFFER_SIZE; i++) { //stereo bypassl[i] += tmpwavel[i] * NoteVoicePar[nvoice].Volume * NoteVoicePar[nvoice].Panning * 2.0; bypassr[i] += tmpwaver[i] * NoteVoicePar[nvoice].Volume * (1.0 - NoteVoicePar[nvoice].Panning) * 2.0; } } else for(i = 0; i < SOUND_BUFFER_SIZE; i++) bypassl[i] += tmpwavel[i] * NoteVoicePar[nvoice].Volume; //mono ; } // chech if there is necesary to proces the voice longer (if the Amplitude envelope isn't finished) if(NoteVoicePar[nvoice].AmpEnvelope != NULL) if(NoteVoicePar[nvoice].AmpEnvelope->finished() != 0) KillVoice(nvoice); ; } //Processing Global parameters NoteGlobalPar.GlobalFilterL->filterout(&outl[0]); if(stereo == 0) { //set the right channel=left channel memcpy(outr, outl, SOUND_BUFFER_SIZE * sizeof(REALTYPE)); memcpy(bypassr, bypassl, SOUND_BUFFER_SIZE * sizeof(REALTYPE)); } else NoteGlobalPar.GlobalFilterR->filterout(&outr[0]); for(i = 0; i < SOUND_BUFFER_SIZE; i++) { outl[i] += bypassl[i]; outr[i] += bypassr[i]; } if(ABOVE_AMPLITUDE_THRESHOLD(globaloldamplitude, globalnewamplitude)) { // Amplitude Interpolation for(i = 0; i < SOUND_BUFFER_SIZE; i++) { REALTYPE tmpvol = INTERPOLATE_AMPLITUDE(globaloldamplitude, globalnewamplitude, i, SOUND_BUFFER_SIZE); outl[i] *= tmpvol * NoteGlobalPar.Panning; outr[i] *= tmpvol * (1.0 - NoteGlobalPar.Panning); } } else { for(i = 0; i < SOUND_BUFFER_SIZE; i++) { outl[i] *= globalnewamplitude * NoteGlobalPar.Panning; outr[i] *= globalnewamplitude * (1.0 - NoteGlobalPar.Panning); } } //Apply the punch if(NoteGlobalPar.Punch.Enabled != 0) { for(i = 0; i < SOUND_BUFFER_SIZE; i++) { REALTYPE punchamp = NoteGlobalPar.Punch.initialvalue * NoteGlobalPar.Punch.t + 1.0; outl[i] *= punchamp; outr[i] *= punchamp; NoteGlobalPar.Punch.t -= NoteGlobalPar.Punch.dt; if(NoteGlobalPar.Punch.t < 0.0) { NoteGlobalPar.Punch.Enabled = 0; break; } } } // Apply legato-specific sound signal modifications if(Legato.silent) // Silencer if(Legato.msg != LM_FadeIn) { memset(outl, 0, SOUND_BUFFER_SIZE * sizeof(REALTYPE)); memset(outr, 0, SOUND_BUFFER_SIZE * sizeof(REALTYPE)); } 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) { for(i = 0; i < SOUND_BUFFER_SIZE; i++) { //fade-out REALTYPE tmp = 1.0 - (REALTYPE)i / (REALTYPE)SOUND_BUFFER_SIZE; outl[i] *= tmp; outr[i] *= tmp; } KillNote(); } return 1; } /* * Relase the key (NoteOff) */ void ADnote::relasekey() { int nvoice; for(nvoice = 0; nvoice < NUM_VOICES; nvoice++) { if(NoteVoicePar[nvoice].Enabled == 0) continue; if(NoteVoicePar[nvoice].AmpEnvelope != NULL) NoteVoicePar[nvoice].AmpEnvelope->relasekey(); if(NoteVoicePar[nvoice].FreqEnvelope != NULL) NoteVoicePar[nvoice].FreqEnvelope->relasekey(); if(NoteVoicePar[nvoice].FilterEnvelope != NULL) NoteVoicePar[nvoice].FilterEnvelope->relasekey(); if(NoteVoicePar[nvoice].FMFreqEnvelope != NULL) NoteVoicePar[nvoice].FMFreqEnvelope->relasekey(); if(NoteVoicePar[nvoice].FMAmpEnvelope != NULL) NoteVoicePar[nvoice].FMAmpEnvelope->relasekey(); } NoteGlobalPar.FreqEnvelope->relasekey(); NoteGlobalPar.FilterEnvelope->relasekey(); NoteGlobalPar.AmpEnvelope->relasekey(); } /* * Check if the note is finished */ int ADnote::finished() const { if(NoteEnabled == ON) return 0; else return 1; }