zynaddsubfx

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

commit 3b06d5ced5d0ea69ab35a8cafe4e7474c0c13a2b
parent 5c938b79f7353432fe473c8841b80a446789b0b4
Author: Friedolino <[email protected]>
Date:   Wed, 22 Apr 2020 12:09:15 +0200

adsynth more comments and small fixes

Diffstat:
Msrc/Synth/ADnote.cpp | 26+++++++++++++++-----------
Asrc/Synth/ADnote.cpp.orig | 2065+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 2080 insertions(+), 11 deletions(-)

diff --git a/src/Synth/ADnote.cpp b/src/Synth/ADnote.cpp @@ -58,10 +58,9 @@ ADnote::ADnote(ADnoteParameters *pars_, const SynthParams &spars, if(pars.GlobalPar.PPanning == 0) NoteGlobalPar.Panning = getRandomFloat(); - else + else NoteGlobalPar.Panning = pars.GlobalPar.PPanning / 128.0f; - NoteGlobalPar.Fadein_adjustment = pars.GlobalPar.Fadein_adjustment / (float)FADEIN_ADJUSTMENT_SCALE; NoteGlobalPar.Fadein_adjustment *= NoteGlobalPar.Fadein_adjustment; @@ -1222,18 +1221,22 @@ inline void ADnote::ComputeVoiceOscillator_LinearInterpolation(int nvoice) Voice& vce = NoteVoicePar[nvoice]; for(int k = 0; k < vce.unison_size; ++k) { int poshi = vce.oscposhi[k]; - int poslo = (int)(vce.oscposlo[k] * (0x01000000)); + // convert floating point fractional part (sample interval phase) + // with range [0.0 ... 1.0] to fixed point with 1 digit is 2^-24 + // by multiplying with precalculated 2^24 and casting to integer: + int poslo = (int)(vce.oscposlo[k] * 16777216.0f); int freqhi = vce.oscfreqhi[k]; - int freqlo = (int)(vce.oscfreqlo[k] * (0x01000000)); + // same for phase increment: + int freqlo = (int)(vce.oscfreqlo[k] * 16777216.0f); float *smps = NoteVoicePar[nvoice].OscilSmp; float *tw = tmpwave_unison[k]; assert(vce.oscfreqlo[k] < 1.0f); for(int i = 0; i < synth.buffersize; ++i) { tw[i] = (smps[poshi] * (0x01000000 - poslo) + smps[poshi + 1] * poslo)/(16777216.0f); - poslo += freqlo; - poshi += freqhi + (poslo>>24); - poslo &= 0xffffff; - poshi &= synth.oscilsize - 1; + poslo += freqlo; // increment fractional part (sample interval phase) + poshi += freqhi + (poslo>>24); // add overflow over 24 bits in poslo to poshi + poslo &= 0xffffff; // remove overflow from poslo + poshi &= synth.oscilsize - 1; // remove overflow } vce.oscposhi[k] = poshi; vce.oscposlo[k] = poslo/(16777216.0f); @@ -1282,6 +1285,7 @@ inline void ADnote::ComputeVoiceOscillator_SincInterpolation(int nvoice) 0.004273442181254887f, 0.0010596256917418426f }; + Voice& vce = NoteVoicePar[nvoice]; @@ -1292,7 +1296,7 @@ inline void ADnote::ComputeVoiceOscillator_SincInterpolation(int nvoice) int freqlo = (int)(vce.oscfreqlo[k] * (1<<24)); int ovsmpfreqhi = vce.oscfreqhi[k] / 2; int ovsmpfreqlo = (int)((vce.oscfreqlo[k] / 2) * (1<<24)); - + int ovsmpposlo; int ovsmpposhi; int uflow; @@ -1300,7 +1304,7 @@ inline void ADnote::ComputeVoiceOscillator_SincInterpolation(int nvoice) float *tw = tmpwave_unison[k]; assert(vce.oscfreqlo[k] < 1.0f); float out = 0; - + for(int i = 0; i < synth.buffersize; ++i) { ovsmpposlo = poslo - (LENGTHOF(kernel)-1)/2 * ovsmpfreqlo; uflow = ovsmpposlo>>24; @@ -1312,7 +1316,7 @@ inline void ADnote::ComputeVoiceOscillator_SincInterpolation(int nvoice) out += kernel[l] * ( smps[ovsmpposhi] * ((1<<24) - ovsmpposlo) + smps[ovsmpposhi + 1] * ovsmpposlo)/(1.0f*(1<<24)); - // advance to next sample + // advance to next kernel sample ovsmpposlo += ovsmpfreqlo; ovsmpposhi += ovsmpfreqhi + (ovsmpposlo>>24); // add the 24-bit overflow ovsmpposlo &= 0xffffff; diff --git a/src/Synth/ADnote.cpp.orig b/src/Synth/ADnote.cpp.orig @@ -0,0 +1,2065 @@ +/* + 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 the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. +*/ + +#include <cmath> +#include <cstdlib> +#include <cstdio> +#include <cstring> +#include <cassert> +#include <stdint.h> + +#include "../globals.h" +#include "../Misc/Util.h" +#include "../Misc/Allocator.h" +#include "../Params/ADnoteParameters.h" +#include "../Containers/ScratchString.h" +#include "../Containers/NotePool.h" +#include "ModFilter.h" +#include "OscilGen.h" +#include "ADnote.h" + +#define LENGTHOF(x) ((int)(sizeof(x)/sizeof(x[0]))) + +namespace zyn { +ADnote::ADnote(ADnoteParameters *pars_, const SynthParams &spars, + WatchManager *wm, const char *prefix) + :SynthNote(spars), watch_be4_add(wm, prefix, "noteout/be4_mix"), watch_after_add(wm,prefix,"noteout/after_mix"), + watch_punch(wm, prefix, "noteout/punch"), watch_legato(wm, prefix, "noteout/legato"), pars(*pars_) +{ + memory.beginTransaction(); + tmpwavel = memory.valloc<float>(synth.buffersize); + tmpwaver = memory.valloc<float>(synth.buffersize); + bypassl = memory.valloc<float>(synth.buffersize); + bypassr = memory.valloc<float>(synth.buffersize); + + ADnoteParameters &pars = *pars_; + portamento = spars.portamento; + note_log2_freq = spars.note_log2_freq; + NoteEnabled = ON; + velocity = spars.velocity; + initial_seed = spars.seed; + current_prng_state = spars.seed; + 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 = getRandomFloat(); + else + NoteGlobalPar.Panning = pars.GlobalPar.PPanning / 128.0f; + + NoteGlobalPar.Fadein_adjustment = + pars.GlobalPar.Fadein_adjustment / (float)FADEIN_ADJUSTMENT_SCALE; + NoteGlobalPar.Fadein_adjustment *= NoteGlobalPar.Fadein_adjustment; + if(pars.GlobalPar.PPunchStrength != 0) { + NoteGlobalPar.Punch.Enabled = 1; + NoteGlobalPar.Punch.t = 1.0f; //start from 1.0f and to 0.0f + NoteGlobalPar.Punch.initialvalue = + ((powf(10, 1.5f * pars.GlobalPar.PPunchStrength / 127.0f) - 1.0f) + * VelF(velocity, + pars.GlobalPar.PPunchVelocitySensing)); + float time = + powf(10, 3.0f * pars.GlobalPar.PPunchTime / 127.0f) / 10000.0f; //0.1f .. 100 ms + float stretch = powf(440.0f / powf(2.0f, spars.note_log2_freq), + pars.GlobalPar.PPunchStretch / 64.0f); + NoteGlobalPar.Punch.dt = 1.0f / (time * synth.samplerate_f * stretch); + } + else + NoteGlobalPar.Punch.Enabled = 0; + + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) + setupVoice(nvoice); + + max_unison = 1; + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) + if(NoteVoicePar[nvoice].unison_size > max_unison) + max_unison = NoteVoicePar[nvoice].unison_size; + + + tmpwave_unison = memory.valloc<float*>(max_unison); + for(int k = 0; k < max_unison; ++k) { + tmpwave_unison[k] = memory.valloc<float>(synth.buffersize); + memset(tmpwave_unison[k], 0, synth.bufferbytes); + } + + initparameters(wm, prefix); + memory.endTransaction(); +} + +void ADnote::setupVoice(int nvoice) +{ + auto &param = pars.VoicePar[nvoice]; + auto &voice = NoteVoicePar[nvoice]; + + + for (int i = 0; i < 14; i++) + voice.pinking[i] = 0.0; + + param.OscilSmp->newrandseed(prng()); + voice.OscilSmp = NULL; + voice.FMSmp = NULL; + voice.VoiceOut = NULL; + + voice.FMVoice = -1; + voice.unison_size = 1; + + if(!pars.VoicePar[nvoice].Enabled) { + voice.Enabled = OFF; + return; //the voice is disabled + } + NoteVoicePar[nvoice].AAEnabled = + pars.VoicePar[nvoice].PAAEnabled; + + const int BendAdj = pars.VoicePar[nvoice].PBendAdjust - 64; + if (BendAdj % 24 == 0) + voice.BendAdjust = BendAdj / 24; + else + voice.BendAdjust = BendAdj / 24.0f; + + const float offset_val = (param.POffsetHz - 64)/64.0f; + voice.OffsetHz = 15.0f*(offset_val * sqrtf(fabsf(offset_val))); + + voice.unison_stereo_spread = + pars.VoicePar[nvoice].Unison_stereo_spread / 127.0f; + + int unison = setupVoiceUnison(nvoice); + + + voice.oscfreqhi = memory.valloc<int>(unison); + voice.oscfreqlo = memory.valloc<float>(unison); + + voice.oscfreqhiFM = memory.valloc<unsigned int>(unison); + voice.oscfreqloFM = memory.valloc<float>(unison); + voice.oscposhi = memory.valloc<int>(unison); + voice.oscposlo = memory.valloc<float>(unison); + voice.oscposhiFM = memory.valloc<unsigned int>(unison); + voice.oscposloFM = memory.valloc<float>(unison); + + voice.Enabled = ON; + voice.fixedfreq = pars.VoicePar[nvoice].Pfixedfreq; + voice.fixedfreqET = pars.VoicePar[nvoice].PfixedfreqET; + + setupVoiceDetune(nvoice); + + for(int k = 0; k < unison; ++k) { + voice.oscposhi[k] = 0; + voice.oscposlo[k] = 0.0f; + voice.oscposhiFM[k] = 0; + voice.oscposloFM[k] = 0.0f; + } + + //the extra points contains the first point + voice.OscilSmp = + memory.valloc<float>(synth.oscilsize + OSCIL_SMP_EXTRA_SAMPLES); + + //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(prng()); + int oscposhi_start = + pars.VoicePar[vc].OscilSmp->get(NoteVoicePar[nvoice].OscilSmp, + getvoicebasefreq(nvoice), + pars.VoicePar[nvoice].Presonance); + + // This code was planned for biasing the carrier in MOD_RING + // but that's on hold for the moment. Disabled 'cos small + // machines run this stuff too. + // + // //Find range of generated wave + // float min = NoteVoicePar[nvoice].OscilSmp[0]; + // float max = min; + // float *smpls = &(NoteVoicePar[nvoice].OscilSmp[1]); + // for (int i = synth.oscilsize-1; i--; smpls++) + // if (*smpls > max) + // max = *smpls; + // else if (*smpls < min) + // min = *smpls; + // NoteVoicePar[nvoice].OscilSmpMin = min; + // NoteVoicePar[nvoice].OscilSmpMax = max; + + //I store the first elements to the last position for speedups + for(int i = 0; i < OSCIL_SMP_EXTRA_SAMPLES; ++i) + voice.OscilSmp[synth.oscilsize + i] = voice.OscilSmp[i]; + + voice.phase_offset = (int)((pars.VoicePar[nvoice].Poscilphase + - 64.0f) / 128.0f * synth.oscilsize + synth.oscilsize * 4); + oscposhi_start += NoteVoicePar[nvoice].phase_offset; + + int kth_start = oscposhi_start; + for(int k = 0; k < unison; ++k) { + voice.oscposhi[k] = kth_start % synth.oscilsize; + //put random starting point for other subvoices + kth_start = oscposhi_start + + (int)(RND * pars.VoicePar[nvoice].Unison_phase_randomness / + 127.0f * (synth.oscilsize - 1)); + } + + voice.FreqLfo = NULL; + voice.FreqEnvelope = NULL; + + voice.AmpLfo = NULL; + voice.AmpEnvelope = NULL; + + voice.Filter = NULL; + voice.FilterEnvelope = NULL; + voice.FilterLfo = NULL; + + voice.filterbypass = param.Pfilterbypass; + + setupVoiceMod(nvoice); + + voice.FMVoice = param.PFMVoice; + voice.FMFreqEnvelope = NULL; + voice.FMAmpEnvelope = NULL; + + voice.FMoldsmp = memory.valloc<float>(unison); + for(int k = 0; k < unison; ++k) + voice.FMoldsmp[k] = 0.0f; //this is for FM (integration) + + voice.firsttick = 1; + voice.DelayTicks = + (int)((expf(param.PDelay / 127.0f * logf(50.0f)) + - 1.0f) / synth.buffersize_f / 10.0f * synth.samplerate_f); +} + +int ADnote::setupVoiceUnison(int nvoice) +{ + auto &voice = NoteVoicePar[nvoice]; + + int unison = pars.VoicePar[nvoice].Unison_size; + if(unison < 1) + unison = 1; + + bool is_pwm = pars.VoicePar[nvoice].PFMEnabled == FMTYPE::PW_MOD; + + if (pars.VoicePar[nvoice].Type != 0) { + // Since noise unison of greater than two is touch goofy... + if (unison > 2) + unison = 2; + } else if (is_pwm) { + /* Pulse width mod uses pairs of subvoices. */ + unison *= 2; + // This many is likely to sound like noise anyhow. + if (unison > 64) + unison = 64; + } + + //compute unison + voice.unison_size = unison; + + voice.unison_base_freq_rap = memory.valloc<float>(unison); + voice.unison_freq_rap = memory.valloc<float>(unison); + voice.unison_invert_phase = memory.valloc<bool>(unison); + const float unison_spread = + pars.getUnisonFrequencySpreadCents(nvoice); + const float unison_real_spread = powf(2.0f, (unison_spread * 0.5f) / 1200.0f); + const float unison_vibratto_a = + pars.VoicePar[nvoice].Unison_vibratto / 127.0f; //0.0f .. 1.0f + + const int true_unison = unison / (is_pwm ? 2 : 1); + switch(true_unison) { + case 1: + voice.unison_base_freq_rap[0] = 1.0f; //if the unison is not used, always make the only subvoice to have the default note + break; + case 2: //unison for 2 subvoices + voice.unison_base_freq_rap[0] = 1.0f / unison_real_spread; + voice.unison_base_freq_rap[1] = unison_real_spread; + break; + default: //unison for more than 2 subvoices + { + float unison_values[true_unison]; + float min = -1e-6f, max = 1e-6f; + for(int k = 0; k < true_unison; ++k) { + float step = (k / (float) (true_unison - 1)) * 2.0f - 1.0f; //this makes the unison spread more uniform + float val = step + (RND * 2.0f - 1.0f) / (true_unison - 1); + unison_values[k] = val; + if (min > val) { + min = val; + } + if (max < val) { + max = val; + } + } + const float diff = max - min; + for(int k = 0; k < true_unison; ++k) { + unison_values[k] = + (unison_values[k] - (max + min) * 0.5f) / diff; //the lowest value will be -1 and the highest will be 1 + voice.unison_base_freq_rap[k] = + powf(2.0f, (unison_spread * unison_values[k]) / 1200); + } + } + } + if (is_pwm) + for (int i = true_unison - 1; i >= 0; i--) { + voice.unison_base_freq_rap[2*i + 1] = + voice.unison_base_freq_rap[i]; + voice.unison_base_freq_rap[2*i] = + voice.unison_base_freq_rap[i]; + } + + //unison vibrattos + if(unison > 2 || (!is_pwm && unison > 1)) + for(int k = 0; k < unison; ++k) //reduce the frequency difference for larger vibrattos + voice.unison_base_freq_rap[k] = 1.0f + + (voice.unison_base_freq_rap[k] - 1.0f) + * (1.0f - unison_vibratto_a); + voice.unison_vibratto.step = memory.valloc<float>(unison); + voice.unison_vibratto.position = memory.valloc<float>(unison); + voice.unison_vibratto.amplitude = + (unison_real_spread - 1.0f) * unison_vibratto_a; + + const float increments_per_second = synth.samplerate_f / synth.buffersize_f; + const float vib_speed = pars.VoicePar[nvoice].Unison_vibratto_speed / 127.0f; + const float vibratto_base_period = 0.25f * powf(2.0f, (1.0f - vib_speed) * 4.0f); + for(int k = 0; k < unison; ++k) { + voice.unison_vibratto.position[k] = RND * 1.8f - 0.9f; + //make period to vary randomly from 50% to 200% vibratto base period + const float vibratto_period = vibratto_base_period + * powf(2.0f, RND * 2.0f - 1.0f); + + const float m = (RND < 0.5f ? -1.0f : 1.0f) * + 4.0f / (vibratto_period * increments_per_second); + voice.unison_vibratto.step[k] = m; + + // Ugly, but the alternative is likely uglier. + if (is_pwm) + for (int i = 0; i < unison; i += 2) { + voice.unison_vibratto.step[i+1] = + voice.unison_vibratto.step[i]; + voice.unison_vibratto.position[i+1] = + voice.unison_vibratto.position[i]; + } + } + + if(unison <= 2) { //no vibratto for a single voice + if (is_pwm) { + voice.unison_vibratto.step[1] = 0.0f; + voice.unison_vibratto.position[1] = 0.0f; + } + if (is_pwm || unison == 1) { + voice.unison_vibratto.step[0] = 0.0f; + voice.unison_vibratto.position[0] = 0.0f; + voice.unison_vibratto.amplitude = 0.0f; + } + } + + //phase invert for unison + voice.unison_invert_phase[0] = false; + if(unison != 1) { + int inv = pars.VoicePar[nvoice].Unison_invert_phase; + switch(inv) { + case 0: + for(int k = 0; k < unison; ++k) + voice.unison_invert_phase[k] = false; + break; + case 1: + for(int k = 0; k < unison; ++k) + voice.unison_invert_phase[k] = (RND > 0.5f); + break; + default: + for(int k = 0; k < unison; ++k) + voice.unison_invert_phase[k] = + (k % inv == 0) ? true : false; + break; + } + } + return unison; +} + +void ADnote::setupVoiceDetune(int nvoice) +{ + //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); +} + +void ADnote::setupVoiceMod(int nvoice, bool first_run) +{ + auto &param = pars.VoicePar[nvoice]; + auto &voice = NoteVoicePar[nvoice]; + float FMVolume; + + if (param.Type != 0) + voice.FMEnabled = FMTYPE::NONE; + else + voice.FMEnabled = param.PFMEnabled; + + voice.FMFreqFixed = param.PFMFixedFreq; + + //Triggers when a user enables modulation on a running voice + if(!first_run && voice.FMEnabled != FMTYPE::NONE && voice.FMSmp == NULL && voice.FMVoice < 0) { + param.FMSmp->newrandseed(prng()); + voice.FMSmp = memory.valloc<float>(synth.oscilsize + OSCIL_SMP_EXTRA_SAMPLES); + memset(voice.FMSmp, 0, sizeof(float)*(synth.oscilsize + OSCIL_SMP_EXTRA_SAMPLES)); + int vc = nvoice; + if(param.PextFMoscil != -1) + vc = param.PextFMoscil; + + float tmp = 1.0f; + if((pars.VoicePar[vc].FMSmp->Padaptiveharmonics != 0) + || (voice.FMEnabled == FMTYPE::MIX) + || (voice.FMEnabled == FMTYPE::RING_MOD)) + tmp = getFMvoicebasefreq(nvoice); + + if(!pars.GlobalPar.Hrandgrouping) + pars.VoicePar[vc].FMSmp->newrandseed(prng()); + + for(int k = 0; k < voice.unison_size; ++k) + voice.oscposhiFM[k] = (voice.oscposhi[k] + + pars.VoicePar[vc].FMSmp->get( + voice.FMSmp, tmp)) + % synth.oscilsize; + + for(int i = 0; i < OSCIL_SMP_EXTRA_SAMPLES; ++i) + voice.FMSmp[synth.oscilsize + i] = voice.FMSmp[i]; + int oscposhiFM_add = + (int)((param.PFMoscilphase + - 64.0f) / 128.0f * synth.oscilsize + + synth.oscilsize * 4); + for(int k = 0; k < voice.unison_size; ++k) { + voice.oscposhiFM[k] += oscposhiFM_add; + voice.oscposhiFM[k] %= synth.oscilsize; + } + } + + + //Compute the Voice's modulator volume (incl. damping) + float fmvoldamp = powf(440.0f / getvoicebasefreq(nvoice), + param.PFMVolumeDamp / 64.0f - 1.0f); + const float fmvolume_ = param.FMvolume / 100.0f; + switch(voice.FMEnabled) { + case FMTYPE::PHASE_MOD: + case FMTYPE::PW_MOD: + fmvoldamp = powf(440.0f / getvoicebasefreq(nvoice), + param.PFMVolumeDamp / 64.0f); + FMVolume = (expf(fmvolume_ * FM_AMP_MULTIPLIER) - 1.0f) + * fmvoldamp * 4.0f; + break; + case FMTYPE::FREQ_MOD: + FMVolume = (expf(fmvolume_ * FM_AMP_MULTIPLIER) - 1.0f) + * fmvoldamp * 4.0f; + break; + default: + if(fmvoldamp > 1.0f) + fmvoldamp = 1.0f; + FMVolume = fmvolume_ * fmvoldamp; + break; + } + + //Voice's modulator velocity sensing + voice.FMVolume = FMVolume * + VelF(velocity, pars.VoicePar[nvoice].PFMVelocityScaleFunction); +} + +SynthNote *ADnote::cloneLegato(void) +{ + SynthParams sp{memory, ctl, synth, time, velocity, + (bool)portamento, legato.param.note_log2_freq, true, + initial_seed }; + return memory.alloc<ADnote>(&pars, sp); +} + +// 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::legatonote(const LegatoParams &lpars) +{ + //ADnoteParameters &pars = *partparams; + // Manage legato stuff + if(legato.update(lpars)) + return; + + portamento = lpars.portamento; + note_log2_freq = lpars.note_log2_freq; + initial_seed = lpars.seed; + current_prng_state = lpars.seed; + + if(lpars.velocity > 1.0f) + velocity = 1.0f; + else + velocity = lpars.velocity; + + const float basefreq = powf(2.0f, note_log2_freq); + + NoteGlobalPar.Detune = getdetune(pars.GlobalPar.PDetuneType, + pars.GlobalPar.PCoarseDetune, + pars.GlobalPar.PDetune); + bandwidthDetuneMultiplier = pars.getBandwidthDetuneMultiplier(); + + if(pars.GlobalPar.PPanning) + NoteGlobalPar.Panning = pars.GlobalPar.PPanning / 128.0f; + + NoteGlobalPar.Filter->updateSense(velocity, + pars.GlobalPar.PFilterVelocityScale, + pars.GlobalPar.PFilterVelocityScaleFunction); + + + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + auto &voice = NoteVoicePar[nvoice]; + float FMVolume; + + if(voice.Enabled == OFF) + continue; //(gf) Stay the same as first note in legato. + + voice.fixedfreq = pars.VoicePar[nvoice].Pfixedfreq; + voice.fixedfreqET = pars.VoicePar[nvoice].PfixedfreqET; + + //use the Globalpars.detunetype if the detunetype is 0 + if(pars.VoicePar[nvoice].PDetuneType != 0) { + voice.Detune = getdetune( + pars.VoicePar[nvoice].PDetuneType, + pars.VoicePar[nvoice].PCoarseDetune, + 8192); //coarse detune + voice.FineDetune = getdetune( + pars.VoicePar[nvoice].PDetuneType, + 0, + pars.VoicePar[nvoice].PDetune); //fine detune + } + else { + voice.Detune = getdetune( + pars.GlobalPar.PDetuneType, + pars.VoicePar[nvoice].PCoarseDetune, + 8192); //coarse detune + voice.FineDetune = getdetune( + pars.GlobalPar.PDetuneType, + 0, + pars.VoicePar[nvoice].PDetune); //fine detune + } + if(pars.VoicePar[nvoice].PFMDetuneType != 0) + voice.FMDetune = getdetune( + pars.VoicePar[nvoice].PFMDetuneType, + pars.VoicePar[nvoice].PFMCoarseDetune, + pars.VoicePar[nvoice].PFMDetune); + else + voice.FMDetune = getdetune( + pars.GlobalPar.PDetuneType, + pars.VoicePar[nvoice].PFMCoarseDetune, + pars.VoicePar[nvoice].PFMDetune); + + auto &voiceFilter = voice.Filter; + if(voiceFilter) { + const auto &vce = pars.VoicePar[nvoice]; + voiceFilter->updateSense(velocity, vce.PFilterVelocityScale, + vce.PFilterVelocityScaleFunction); + } + + voice.filterbypass = + pars.VoicePar[nvoice].Pfilterbypass; + + + voice.FMVoice = pars.VoicePar[nvoice].PFMVoice; + + //Compute the Voice's modulator volume (incl. damping) + float fmvoldamp = powf(440.0f / getvoicebasefreq(nvoice), + pars.VoicePar[nvoice].PFMVolumeDamp / 64.0f + - 1.0f); + + switch(voice.FMEnabled) { + case FMTYPE::PHASE_MOD: + case FMTYPE::PW_MOD: + fmvoldamp = + powf(440.0f / getvoicebasefreq( + nvoice), pars.VoicePar[nvoice].PFMVolumeDamp + / 64.0f); + FMVolume = + (expf(pars.VoicePar[nvoice].FMvolume / 100.0f + * FM_AMP_MULTIPLIER) - 1.0f) * fmvoldamp * 4.0f; + break; + case FMTYPE::FREQ_MOD: + FMVolume = + (expf(pars.VoicePar[nvoice].FMvolume / 100.0f + * FM_AMP_MULTIPLIER) - 1.0f) * fmvoldamp * 4.0f; + break; + default: + if(fmvoldamp > 1.0f) + fmvoldamp = 1.0f; + FMVolume = + pars.VoicePar[nvoice].FMvolume + / 100.0f * fmvoldamp; + break; + } + + //Voice's modulator velocity sensing + voice.FMVolume = FMVolume * + VelF(velocity, + pars.VoicePar[nvoice].PFMVelocityScaleFunction); + } + /// initparameters(); + + /////////////// + // Altered content of initparameters(): + + int tmp[NUM_VOICES]; + + NoteGlobalPar.Volume = dB2rap(pars.GlobalPar.Volume) //-60 dB .. 20 dB + * VelF( + velocity, + pars.GlobalPar.PAmpVelocityScaleFunction); //velocity sensing + + { + auto *filter = NoteGlobalPar.Filter; + filter->updateSense(velocity, pars.GlobalPar.PFilterVelocityScale, + pars.GlobalPar.PFilterVelocityScaleFunction); + filter->updateNoteFreq(basefreq); + } + + // Forbids the Modulation Voice to be greater or equal than voice + for(int i = 0; i < NUM_VOICES; ++i) + if(NoteVoicePar[i].FMVoice >= i) + NoteVoicePar[i].FMVoice = -1; + + // Voice Parameter init + for(unsigned nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + Voice& vce = NoteVoicePar[nvoice]; + if(NoteVoicePar[nvoice].Enabled == 0) + continue; + + NoteVoicePar[nvoice].noisetype = pars.VoicePar[nvoice].Type; + /* Voice Amplitude Parameters Init */ + NoteVoicePar[nvoice].Volume = + dB2rap(pars.VoicePar[nvoice].volume) // -60 dB .. 0 dB + * VelF(velocity, + pars.VoicePar[nvoice].PAmpVelocityScaleFunction); //velocity + if(pars.VoicePar[nvoice].volume == -60.0) + NoteVoicePar[nvoice].Volume = 0; + + if(pars.VoicePar[nvoice].PVolumeminus != 0) + NoteVoicePar[nvoice].Volume = -NoteVoicePar[nvoice].Volume; + + NoteVoicePar[nvoice].AAEnabled = + pars.VoicePar[nvoice].PAAEnabled; + + if(pars.VoicePar[nvoice].PPanning == 0) { + NoteVoicePar[nvoice].Panning = getRandomFloat(); + } else + NoteVoicePar[nvoice].Panning = + pars.VoicePar[nvoice].PPanning / 128.0f; + + vce.newamplitude = 1.0f; + if(pars.VoicePar[nvoice].PAmpEnvelopeEnabled + && NoteVoicePar[nvoice].AmpEnvelope) + vce.newamplitude *= NoteVoicePar[nvoice].AmpEnvelope->envout_dB(); + + + if(pars.VoicePar[nvoice].PAmpLfoEnabled && NoteVoicePar[nvoice].AmpLfo) + vce.newamplitude *= NoteVoicePar[nvoice].AmpLfo->amplfoout(); + + auto *voiceFilter = NoteVoicePar[nvoice].Filter; + if(voiceFilter) { + voiceFilter->updateSense(velocity, pars.VoicePar[nvoice].PFilterVelocityScale, + pars.VoicePar[nvoice].PFilterVelocityScaleFunction); + voiceFilter->updateNoteFreq(basefreq); + } + + /* Voice Modulation Parameters Init */ + if((NoteVoicePar[nvoice].FMEnabled != FMTYPE::NONE) + && (NoteVoicePar[nvoice].FMVoice < 0)) { + pars.VoicePar[nvoice].FMSmp->newrandseed(prng()); + + //Perform Anti-aliasing only on MIX or RING MODULATION + + int vc = nvoice; + if(pars.VoicePar[nvoice].PextFMoscil != -1) + vc = pars.VoicePar[nvoice].PextFMoscil; + + if(!pars.GlobalPar.Hrandgrouping) + pars.VoicePar[vc].FMSmp->newrandseed(prng()); + + for(int i = 0; i < OSCIL_SMP_EXTRA_SAMPLES; ++i) + NoteVoicePar[nvoice].FMSmp[synth.oscilsize + i] = + NoteVoicePar[nvoice].FMSmp[i]; + } + + vce.FMnewamplitude = NoteVoicePar[nvoice].FMVolume + * ctl.fmamp.relamp; + + if(pars.VoicePar[nvoice].PFMAmpEnvelopeEnabled + && NoteVoicePar[nvoice].FMAmpEnvelope) + vce.FMnewamplitude *= + NoteVoicePar[nvoice].FMAmpEnvelope->envout_dB(); + } + + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + for(unsigned i = nvoice + 1; i < NUM_VOICES; ++i) + tmp[i] = 0; + for(unsigned i = nvoice + 1; i < NUM_VOICES; ++i) + if((NoteVoicePar[i].FMVoice == nvoice) && (tmp[i] == 0)) + tmp[i] = 1; + } +} + + +/* + * Kill a voice of ADnote + */ +void ADnote::KillVoice(int nvoice) +{ + auto &voice = NoteVoicePar[nvoice]; + + memory.devalloc(voice.oscfreqhi); + memory.devalloc(voice.oscfreqlo); + memory.devalloc(voice.oscfreqhiFM); + memory.devalloc(voice.oscfreqloFM); + memory.devalloc(voice.oscposhi); + memory.devalloc(voice.oscposlo); + memory.devalloc(voice.oscposhiFM); + memory.devalloc(voice.oscposloFM); + + memory.devalloc(voice.unison_base_freq_rap); + memory.devalloc(voice.unison_freq_rap); + memory.devalloc(voice.unison_invert_phase); + memory.devalloc(voice.FMoldsmp); + memory.devalloc(voice.unison_vibratto.step); + memory.devalloc(voice.unison_vibratto.position); + + NoteVoicePar[nvoice].kill(memory, synth); +} + +/* + * Kill the note + */ +void ADnote::KillNote() +{ + for(unsigned nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + if(NoteVoicePar[nvoice].Enabled == ON) + KillVoice(nvoice); + + if(NoteVoicePar[nvoice].VoiceOut) + memory.dealloc(NoteVoicePar[nvoice].VoiceOut); + } + + NoteGlobalPar.kill(memory); + + NoteEnabled = OFF; +} + +ADnote::~ADnote() +{ + if(NoteEnabled == ON) + KillNote(); + memory.devalloc(tmpwavel); + memory.devalloc(tmpwaver); + memory.devalloc(bypassl); + memory.devalloc(bypassr); + for(int k = 0; k < max_unison; ++k) + memory.devalloc(tmpwave_unison[k]); + memory.devalloc(tmpwave_unison); +} + + +/* + * Init the parameters + */ +void ADnote::initparameters(WatchManager *wm, const char *prefix) +{ + int tmp[NUM_VOICES]; + ScratchString pre = prefix; + const float basefreq = powf(2.0f, note_log2_freq); + + // Global Parameters + NoteGlobalPar.initparameters(pars.GlobalPar, synth, + time, + memory, basefreq, velocity, + stereo, wm, prefix); + + NoteGlobalPar.AmpEnvelope->envout_dB(); //discard the first envelope output + globalnewamplitude = NoteGlobalPar.Volume + * NoteGlobalPar.AmpEnvelope->envout_dB() + * NoteGlobalPar.AmpLfo->amplfoout(); + + // Forbids the Modulation Voice to be greater or equal than voice + for(int i = 0; i < NUM_VOICES; ++i) + if(NoteVoicePar[i].FMVoice >= i) + NoteVoicePar[i].FMVoice = -1; + + // Voice Parameter init + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + Voice &vce = NoteVoicePar[nvoice]; + ADnoteVoiceParam &param = pars.VoicePar[nvoice]; + + if(vce.Enabled == 0) + continue; + + vce.noisetype = param.Type; + /* Voice Amplitude Parameters Init */ + vce.Volume = dB2rap(param.volume) // -60dB..0dB + * VelF(velocity, param.PAmpVelocityScaleFunction); + if(param.volume == -60.0f) + vce.Volume = 0; + + if(param.PVolumeminus) + vce.Volume = -vce.Volume; + + if(param.PPanning == 0) { + vce.Panning = getRandomFloat(); + } else + vce.Panning = param.PPanning / 128.0f; + + vce.newamplitude = 1.0f; + if(param.PAmpEnvelopeEnabled) { + vce.AmpEnvelope = memory.alloc<Envelope>(*param.AmpEnvelope, + basefreq, synth.dt(), wm, + (pre+"VoicePar"+nvoice+"/AmpEnvelope/").c_str); + vce.AmpEnvelope->envout_dB(); //discard the first envelope sample + vce.newamplitude *= vce.AmpEnvelope->envout_dB(); + } + + if(param.PAmpLfoEnabled) { + vce.AmpLfo = memory.alloc<LFO>(*param.AmpLfo, basefreq, time, wm, + (pre+"VoicePar"+nvoice+"/AmpLfo/").c_str); + vce.newamplitude *= vce.AmpLfo->amplfoout(); + } + + /* Voice Frequency Parameters Init */ + if(param.PFreqEnvelopeEnabled) + vce.FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, + basefreq, synth.dt(), wm, + (pre+"VoicePar"+nvoice+"/FreqEnvelope/").c_str); + + if(param.PFreqLfoEnabled) + vce.FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time, wm, + (pre+"VoicePar"+nvoice+"/FreqLfo/").c_str); + + /* Voice Filter Parameters Init */ + if(param.PFilterEnabled) { + vce.Filter = memory.alloc<ModFilter>(*param.VoiceFilter, synth, time, memory, stereo, + basefreq); + vce.Filter->updateSense(velocity, param.PFilterVelocityScale, + param.PFilterVelocityScaleFunction); + + + if(param.PFilterEnvelopeEnabled) { + vce.FilterEnvelope = + memory.alloc<Envelope>(*param.FilterEnvelope, + basefreq, synth.dt(), wm, + (pre+"VoicePar"+nvoice+"/FilterEnvelope/").c_str); + vce.Filter->addMod(*vce.FilterEnvelope); + } + + if(param.PFilterLfoEnabled) { + vce.FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time, wm, + (pre+"VoicePar"+nvoice+"/FilterLfo/").c_str); + vce.Filter->addMod(*vce.FilterLfo); + } + } + + /* Voice Modulation Parameters Init */ + if((vce.FMEnabled != FMTYPE::NONE) && (vce.FMVoice < 0)) { + param.FMSmp->newrandseed(prng()); + vce.FMSmp = memory.valloc<float>(synth.oscilsize + OSCIL_SMP_EXTRA_SAMPLES); + + //Perform Anti-aliasing only on MIX or RING MODULATION + + int vc = nvoice; + if(param.PextFMoscil != -1) + vc = param.PextFMoscil; + + float tmp = 1.0f; + if((pars.VoicePar[vc].FMSmp->Padaptiveharmonics != 0) + || (vce.FMEnabled == FMTYPE::MIX) + || (vce.FMEnabled == FMTYPE::RING_MOD)) + tmp = getFMvoicebasefreq(nvoice); + + if(!pars.GlobalPar.Hrandgrouping) + pars.VoicePar[vc].FMSmp->newrandseed(prng()); + + for(int k = 0; k < vce.unison_size; ++k) + vce.oscposhiFM[k] = (vce.oscposhi[k] + + pars.VoicePar[vc].FMSmp->get( + vce.FMSmp, tmp)) + % synth.oscilsize; + + for(int i = 0; i < OSCIL_SMP_EXTRA_SAMPLES; ++i) + vce.FMSmp[synth.oscilsize + i] = vce.FMSmp[i]; + int oscposhiFM_add = + (int)((param.PFMoscilphase + - 64.0f) / 128.0f * synth.oscilsize + + synth.oscilsize * 4); + for(int k = 0; k < vce.unison_size; ++k) { + vce.oscposhiFM[k] += oscposhiFM_add; + vce.oscposhiFM[k] %= synth.oscilsize; + } + } + + if(param.PFMFreqEnvelopeEnabled) + vce.FMFreqEnvelope = memory.alloc<Envelope>(*param.FMFreqEnvelope, + basefreq, synth.dt(), wm, + (pre+"VoicePar"+nvoice+"/FMFreqEnvelope/").c_str); + + vce.FMnewamplitude = vce.FMVolume * ctl.fmamp.relamp; + + if(param.PFMAmpEnvelopeEnabled) { + vce.FMAmpEnvelope = + memory.alloc<Envelope>(*param.FMAmpEnvelope, + basefreq, synth.dt(), wm, + (pre+"VoicePar"+nvoice+"/FMAmpEnvelope/").c_str); + vce.FMnewamplitude *= vce.FMAmpEnvelope->envout_dB(); + } + } + + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + for(int i = nvoice + 1; i < NUM_VOICES; ++i) + tmp[i] = 0; + for(int i = nvoice + 1; i < NUM_VOICES; ++i) + if((NoteVoicePar[i].FMVoice == nvoice) && (tmp[i] == 0)) { + NoteVoicePar[nvoice].VoiceOut = + memory.valloc<float>(synth.buffersize); + tmp[i] = 1; + } + + if(NoteVoicePar[nvoice].VoiceOut) + memset(NoteVoicePar[nvoice].VoiceOut, 0, synth.bufferbytes); + } +} + + +/* + * 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) { + Voice &vce = NoteVoicePar[nvoice]; + if(vce.unison_size == 1) { //no unison + vce.unison_freq_rap[0] = 1.0f; + return; + } + float relbw = ctl.bandwidth.relbw * bandwidthDetuneMultiplier; + for(int k = 0; k < vce.unison_size; ++k) { + float pos = vce.unison_vibratto.position[k]; + float step = vce.unison_vibratto.step[k]; + pos += step; + if(pos <= -1.0f) { + pos = -1.0f; + step = -step; + } + if(pos >= 1.0f) { + pos = 1.0f; + step = -step; + } + float vibratto_val = (pos - 0.333333333f * pos * pos * pos) * 1.5f; //make the vibratto lfo smoother + vce.unison_freq_rap[k] = 1.0f + + ((vce.unison_base_freq_rap[k] + - 1.0f) + vibratto_val + * vce.unison_vibratto.amplitude) + * relbw; + + vce.unison_vibratto.position[k] = pos; + step = vce.unison_vibratto.step[k] = step; + } +} + + +/* + * Computes the frequency of an oscillator + */ +void ADnote::setfreq(int nvoice, float in_freq) +{ + Voice &vce = NoteVoicePar[nvoice]; + for(int k = 0; k < vce.unison_size; ++k) { + float freq = fabsf(in_freq) * vce.unison_freq_rap[k]; + float speed = freq * synth.oscilsize_f / synth.samplerate_f; + if(speed > synth.oscilsize_f) + speed = synth.oscilsize_f; + + F2I(speed, vce.oscfreqhi[k]); + vce.oscfreqlo[k] = speed - floorf(speed); + + } +} + +/* + * Computes the frequency of an modullator oscillator + */ +void ADnote::setfreqFM(int nvoice, float in_freq) +{ + Voice &vce = NoteVoicePar[nvoice]; + for(int k = 0; k < vce.unison_size; ++k) { + float freq = fabsf(in_freq) * vce.unison_freq_rap[k]; + float speed = freq * synth.oscilsize_f / synth.samplerate_f; + if(speed > synth.samplerate_f) + speed = synth.samplerate_f; + + F2I(speed, vce.oscfreqhiFM[k]); + vce.oscfreqloFM[k] = speed - floorf(speed); + } +} + +/* + * Get Voice base frequency + */ +float ADnote::getvoicebasefreq(int nvoice, float adjust_log2) const +{ + const float detune = NoteVoicePar[nvoice].Detune / 100.0f + + NoteVoicePar[nvoice].FineDetune / 100.0f + * ctl.bandwidth.relbw * bandwidthDetuneMultiplier + + NoteGlobalPar.Detune / 100.0f; + + if(NoteVoicePar[nvoice].fixedfreq == 0) { + return powf(2.0f, note_log2_freq + detune / 12.0f + adjust_log2); + } + else { //the fixed freq is enabled + const int fixedfreqET = NoteVoicePar[nvoice].fixedfreqET; + float fixedfreq_log2 = log2f(440.0f); + + if(fixedfreqET != 0) { //if the frequency varies according the keyboard note + float tmp_log2 = (note_log2_freq - fixedfreq_log2) * + (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f); + if(fixedfreqET <= 64) + fixedfreq_log2 += tmp_log2; + else + fixedfreq_log2 += tmp_log2 * log2f(3.0f); + } + return powf(2.0f, fixedfreq_log2 + detune / 12.0f + adjust_log2); + } +} + +/* + * Get Voice's Modullator base frequency + */ +float ADnote::getFMvoicebasefreq(int nvoice) const +{ + return getvoicebasefreq(nvoice, NoteVoicePar[nvoice].FMDetune / 1200.0f); +} + +/* + * Computes all the parameters for each tick + */ +void ADnote::computecurrentparameters() +{ + const float relfreq = getFilterCutoffRelFreq(); + int nvoice; + float voicefreq, voicepitch, FMfreq, + FMrelativepitch, globalpitch; + + globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout() + + NoteGlobalPar.FreqLfo->lfoout() + * ctl.modwheel.relmod); + globaloldamplitude = globalnewamplitude; + globalnewamplitude = NoteGlobalPar.Volume + * NoteGlobalPar.AmpEnvelope->envout_dB() + * NoteGlobalPar.AmpLfo->amplfoout(); + + NoteGlobalPar.Filter->update(relfreq, ctl.filterq.relq); + + //compute the portamento, if it is used by this note + float portamentofreqdelta_log2 = 0.0f; + if(portamento != 0) { //this voice use portamento + portamentofreqdelta_log2 = ctl.portamento.freqdelta_log2; + 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) { + Voice& vce = NoteVoicePar[nvoice]; + if(NoteVoicePar[nvoice].Enabled != ON) + continue; + NoteVoicePar[nvoice].DelayTicks -= 1; + if(NoteVoicePar[nvoice].DelayTicks > 0) + continue; + + compute_unison_freq_rap(nvoice); + + /*******************/ + /* Voice Amplitude */ + /*******************/ + vce.oldamplitude = vce.newamplitude; + vce.newamplitude = 1.0f; + + if(NoteVoicePar[nvoice].AmpEnvelope) + vce.newamplitude *= NoteVoicePar[nvoice].AmpEnvelope->envout_dB(); + + if(NoteVoicePar[nvoice].AmpLfo) + vce.newamplitude *= NoteVoicePar[nvoice].AmpLfo->amplfoout(); + + /****************/ + /* Voice Filter */ + /****************/ + auto *voiceFilter = NoteVoicePar[nvoice].Filter; + if(voiceFilter) { + voiceFilter->update(relfreq, ctl.filterq.relq); + } + + if(NoteVoicePar[nvoice].noisetype == 0) { //compute only if the voice isn't noise + /*******************/ + /* Voice Frequency */ + /*******************/ + voicepitch = 0.0f; + if(NoteVoicePar[nvoice].FreqLfo) + voicepitch += NoteVoicePar[nvoice].FreqLfo->lfoout() / 100.0f + * ctl.bandwidth.relbw; + + if(NoteVoicePar[nvoice].FreqEnvelope) + voicepitch += NoteVoicePar[nvoice].FreqEnvelope->envout() + / 100.0f; + voicefreq = getvoicebasefreq(nvoice, portamentofreqdelta_log2 + + (voicepitch + globalpitch) / 12.0f); //Hz frequency + voicefreq *= + powf(ctl.pitchwheel.relfreq, NoteVoicePar[nvoice].BendAdjust); //change the frequency by the controller + setfreq(nvoice, voicefreq + NoteVoicePar[nvoice].OffsetHz); + + /***************/ + /* Modulator */ + /***************/ + + + if(NoteVoicePar[nvoice].FMEnabled != FMTYPE::NONE) { + FMrelativepitch = NoteVoicePar[nvoice].FMDetune / 100.0f; + if(NoteVoicePar[nvoice].FMFreqEnvelope) + FMrelativepitch += + NoteVoicePar[nvoice].FMFreqEnvelope->envout() / 100.0f; + if (NoteVoicePar[nvoice].FMFreqFixed) + FMfreq = powf(2.0f, FMrelativepitch / 12.0f) * 440.0f; + else + FMfreq = powf(2.0f, FMrelativepitch / 12.0f) * voicefreq; + setfreqFM(nvoice, FMfreq); + + vce.FMoldamplitude = vce.FMnewamplitude; + vce.FMnewamplitude = NoteVoicePar[nvoice].FMVolume + * ctl.fmamp.relamp; + if(NoteVoicePar[nvoice].FMAmpEnvelope) + vce.FMnewamplitude *= + NoteVoicePar[nvoice].FMAmpEnvelope->envout_dB(); + } + } + } +} + + +/* + * Fadein in a way that removes clicks but keep sound "punchy" + */ +inline void ADnote::fadein(float *smps) const +{ + int zerocrossings = 0; + for(int i = 1; i < synth.buffersize; ++i) + if((smps[i - 1] < 0.0f) && (smps[i] > 0.0f)) + zerocrossings++; //this is only the positive crossings + + float tmp = (synth.buffersize_f - 1.0f) / (zerocrossings + 1) / 3.0f; + if(tmp < 8.0f) + tmp = 8.0f; + tmp *= NoteGlobalPar.Fadein_adjustment; + + int n; + F2I(tmp, n); //how many samples is the fade-in + if(n > synth.buffersize) + n = synth.buffersize; + for(int i = 0; i < n; ++i) { //fade-in + float tmp = 0.5f - cosf((float)i / (float) n * PI) * 0.5f; + smps[i] *= tmp; + } +} + +/* + * Computes the Oscillator (Without Modulation) - LinearInterpolation + */ + +/* As the code here is a bit odd due to optimization, here is what happens + * First the current position and frequency are retrieved from the running + * state. These are broken up into high and low portions to indicate how many + * samples are skipped in one step and how many fractional samples are skipped. + * Outside of this method the fractional samples are just handled with floating + * point code, but that's a bit slower than it needs to be. In this code the low + * portions are known to exist between 0.0 and 1.0 and it is known that they are + * stored in single precision floating point IEEE numbers. This implies that + * a maximum of 24 bits are significant. The below code does your standard + * linear interpolation that you'll see throughout this codebase, but by + * sticking to integers for tracking the overflow of the low portion, around 15% + * of the execution time was shaved off in the ADnote test. + */ +inline void ADnote::ComputeVoiceOscillator_LinearInterpolation(int nvoice) +{ +<<<<<<< HEAD + Voice& vce = NoteVoicePar[nvoice]; + for(int k = 0; k < vce.unison_size; ++k) { + int poshi = vce.oscposhi[k]; + int poslo = (int)(vce.oscposlo[k] * (0x01000000)); + int freqhi = vce.oscfreqhi[k]; + int freqlo = (int)(vce.oscfreqlo[k] * (0x01000000)); +======= + for(int k = 0; k < unison_size[nvoice]; ++k) { + int poshi = oscposhi[nvoice][k]; + // convert floating point fractional part (sample interval phase) + // with range [0.0 ... 1.0] to fixed point with 1 digit is 2^-24 + // by multiplying with precalculated 2^24 and casting to integer: + int poslo = (int)(oscposlo[nvoice][k] * 16777216.0f); + int freqhi = oscfreqhi[nvoice][k]; + // same for phase increment: + int freqlo = (int)(oscfreqlo[nvoice][k] * 16777216.0f); +>>>>>>> adsynth more comments and small fixes + float *smps = NoteVoicePar[nvoice].OscilSmp; + float *tw = tmpwave_unison[k]; + assert(vce.oscfreqlo[k] < 1.0f); + for(int i = 0; i < synth.buffersize; ++i) { + tw[i] = (smps[poshi] * (0x01000000 - poslo) + smps[poshi + 1] * poslo)/(16777216.0f); + poslo += freqlo; // increment fractional part (sample interval phase) + poshi += freqhi + (poslo>>24); // add overflow over 24 bits in poslo to poshi + poslo &= 0xffffff; // remove overflow from poslo + poshi &= synth.oscilsize - 1; // remove overflow + } + vce.oscposhi[k] = poshi; + vce.oscposlo[k] = poslo/(16777216.0f); + } +} + + +/* + * Computes the Oscillator (Without Modulation) - windowed sinc Interpolation + */ + +/* As the code here is a bit odd due to optimization, here is what happens + * First the current position and frequency are retrieved from the running + * state. These are broken up into high and low portions to indicate how many + * samples are skipped in one step and how many fractional samples are skipped. + * Outside of this method the fractional samples are just handled with floating + * point code, but that's a bit slower than it needs to be. In this code the low + * portions are known to exist between 0.0 and 1.0 and it is known that they are + * stored in single precision floating point IEEE numbers. This implies that + * a maximum of 24 bits are significant. The below code does your standard + * linear interpolation that you'll see throughout this codebase, but by + * sticking to integers for tracking the overflow of the low portion, around 15% + * of the execution time was shaved off in the ADnote test. + */ +inline void ADnote::ComputeVoiceOscillator_SincInterpolation(int nvoice) +{ + // windowed sinc kernel factor Fs*0.3, rejection 80dB + const float_t kernel[] = { + 0.0010596256917418426f, + 0.004273442181254887f, + 0.0035466063043375785f, + -0.014555483937137638f, + -0.04789321342588484f, + -0.050800020978553066f, + 0.04679847159974432f, + 0.2610646708018185f, + 0.4964802251145513f, + 0.6000513532962539f, + 0.4964802251145513f, + 0.2610646708018185f, + 0.04679847159974432f, + -0.050800020978553066f, + -0.04789321342588484f, + -0.014555483937137638f, + 0.0035466063043375785f, + 0.004273442181254887f, + 0.0010596256917418426f + }; +<<<<<<< HEAD + + + Voice& vce = NoteVoicePar[nvoice]; + for(int k = 0; k < vce.unison_size; ++k) { + int poshi = vce.oscposhi[k]; + int poslo = (int)(vce.oscposlo[k] * (1<<24)); + int freqhi = vce.oscfreqhi[k]; + int freqlo = (int)(vce.oscfreqlo[k] * (1<<24)); + int ovsmpfreqhi = vce.oscfreqhi[k] / 2; + int ovsmpfreqlo = (int)((vce.oscfreqlo[k] / 2) * (1<<24)); + +======= + + for(int k = 0; k < unison_size[nvoice]; ++k) { + int poshi = oscposhi[nvoice][k]; + int poslo = (int)(oscposlo[nvoice][k] * (1<<24)); + int freqhi = oscfreqhi[nvoice][k]; + int freqlo = (int)(oscfreqlo[nvoice][k] * (1<<24)); + int ovsmpfreqhi = oscfreqhi[nvoice][k] / 2; + int ovsmpfreqlo = (int)((oscfreqlo[nvoice][k] / 2) * (1<<24)); + +>>>>>>> adsynth more comments and small fixes + int ovsmpposlo; + int ovsmpposhi; + int uflow; + float *smps = NoteVoicePar[nvoice].OscilSmp; + float *tw = tmpwave_unison[k]; + assert(vce.oscfreqlo[k] < 1.0f); + float out = 0; + + for(int i = 0; i < synth.buffersize; ++i) { + ovsmpposlo = poslo - (LENGTHOF(kernel)-1)/2 * ovsmpfreqlo; + uflow = ovsmpposlo>>24; + ovsmpposhi = poshi - (LENGTHOF(kernel)-1)/2 * ovsmpfreqhi - ((0x00 - uflow) & 0xff); + ovsmpposlo &= 0xffffff; + ovsmpposhi &= synth.oscilsize - 1; + out = 0; + for (int l = 0; l<LENGTHOF(kernel); l++) { + out += kernel[l] * ( + smps[ovsmpposhi] * ((1<<24) - ovsmpposlo) + + smps[ovsmpposhi + 1] * ovsmpposlo)/(1.0f*(1<<24)); + // advance to next kernel sample + ovsmpposlo += ovsmpfreqlo; + ovsmpposhi += ovsmpfreqhi + (ovsmpposlo>>24); // add the 24-bit overflow + ovsmpposlo &= 0xffffff; + ovsmpposhi &= synth.oscilsize - 1; + } + + // advance to next sample + poslo += freqlo; + poshi += freqhi + (poslo>>24); + poslo &= 0xffffff; + poshi &= synth.oscilsize - 1; + + tw[i] = out; + + } + vce.oscposhi[k] = poshi; + vce.oscposlo[k] = poslo/(1.0f*(1<<24)); + } +} + +/* + * Computes the Oscillator (Mixing) + */ +inline void ADnote::ComputeVoiceOscillatorMix(int nvoice) +{ + ComputeVoiceOscillator_LinearInterpolation(nvoice); + + Voice& vce = NoteVoicePar[nvoice]; + if(vce.FMnewamplitude > 1.0f) + vce.FMnewamplitude = 1.0f; + if(vce.FMoldamplitude > 1.0f) + vce.FMoldamplitude = 1.0f; + + if(NoteVoicePar[nvoice].FMVoice >= 0) { + //if I use VoiceOut[] as modullator + int FMVoice = NoteVoicePar[nvoice].FMVoice; + for(int k = 0; k < vce.unison_size; ++k) { + float *tw = tmpwave_unison[k]; + for(int i = 0; i < synth.buffersize; ++i) { + float amp = INTERPOLATE_AMPLITUDE(vce.FMoldamplitude, + vce.FMnewamplitude, + i, + synth.buffersize); + tw[i] = tw[i] + * (1.0f - amp) + amp * NoteVoicePar[FMVoice].VoiceOut[i]; + } + } + } + else + for(int k = 0; k < vce.unison_size; ++k) { + int poshiFM = vce.oscposhiFM[k]; + float posloFM = vce.oscposloFM[k]; + int freqhiFM = vce.oscfreqhiFM[k]; + float freqloFM = vce.oscfreqloFM[k]; + float *tw = tmpwave_unison[k]; + + for(int i = 0; i < synth.buffersize; ++i) { + float amp = INTERPOLATE_AMPLITUDE(vce.FMoldamplitude, + vce.FMnewamplitude, + i, + synth.buffersize); + tw[i] = tw[i] * (1.0f - amp) + amp + * (NoteVoicePar[nvoice].FMSmp[poshiFM] * (1 - posloFM) + + NoteVoicePar[nvoice].FMSmp[poshiFM + 1] * posloFM); + posloFM += freqloFM; + if(posloFM >= 1.0f) { + posloFM -= 1.0f; + poshiFM++; + } + poshiFM += freqhiFM; + poshiFM &= synth.oscilsize - 1; + } + vce.oscposhiFM[k] = poshiFM; + vce.oscposloFM[k] = posloFM; + } +} + +/* + * Computes the Oscillator (Ring Modulation) + */ +inline void ADnote::ComputeVoiceOscillatorRingModulation(int nvoice) +{ + ComputeVoiceOscillator_LinearInterpolation(nvoice); + + Voice& vce = NoteVoicePar[nvoice]; + if(vce.FMnewamplitude > 1.0f) + vce.FMnewamplitude = 1.0f; + if(vce.FMoldamplitude > 1.0f) + vce.FMoldamplitude = 1.0f; + if(NoteVoicePar[nvoice].FMVoice >= 0) + // if I use VoiceOut[] as modullator + for(int k = 0; k < vce.unison_size; ++k) { + float *tw = tmpwave_unison[k]; + for(int i = 0; i < synth.buffersize; ++i) { + float amp = INTERPOLATE_AMPLITUDE(vce.FMoldamplitude, + vce.FMnewamplitude, + i, + synth.buffersize); + int FMVoice = NoteVoicePar[nvoice].FMVoice; + tw[i] *= (1.0f - amp) + amp * NoteVoicePar[FMVoice].VoiceOut[i]; + } + } + else + for(int k = 0; k < vce.unison_size; ++k) { + int poshiFM = vce.oscposhiFM[k]; + float posloFM = vce.oscposloFM[k]; + int freqhiFM = vce.oscfreqhiFM[k]; + float freqloFM = vce.oscfreqloFM[k]; + float *tw = tmpwave_unison[k]; + + for(int i = 0; i < synth.buffersize; ++i) { + float amp = INTERPOLATE_AMPLITUDE(vce.FMoldamplitude, + vce.FMnewamplitude, + i, + synth.buffersize); + tw[i] *= (NoteVoicePar[nvoice].FMSmp[poshiFM] * (1.0f - posloFM) + + NoteVoicePar[nvoice].FMSmp[poshiFM + + 1] * posloFM) * amp + + (1.0f - amp); + posloFM += freqloFM; + if(posloFM >= 1.0f) { + posloFM -= 1.0f; + poshiFM++; + } + poshiFM += freqhiFM; + poshiFM &= synth.oscilsize - 1; + } + vce.oscposhiFM[k] = poshiFM; + vce.oscposloFM[k] = posloFM; + } +} + +/* + * Computes the Oscillator (Phase Modulation or Frequency Modulation) + */ +inline void ADnote::ComputeVoiceOscillatorFrequencyModulation(int nvoice, + FMTYPE FMmode) +{ + Voice& vce = NoteVoicePar[nvoice]; + if(NoteVoicePar[nvoice].FMVoice >= 0) { + //if I use VoiceOut[] as modulator + for(int k = 0; k < vce.unison_size; ++k) { + float *tw = tmpwave_unison[k]; + const float *smps = NoteVoicePar[NoteVoicePar[nvoice].FMVoice].VoiceOut; + if (FMmode == FMTYPE::PW_MOD && (k & 1)) + for (int i = 0; i < synth.buffersize; ++i) + tw[i] = -smps[i]; + else + memcpy(tw, smps, synth.bufferbytes); + } + } else { + //Compute the modulator and store it in tmpwave_unison[][] + for(int k = 0; k < vce.unison_size; ++k) { + int poshiFM = vce.oscposhiFM[k]; + int posloFM = (int)(vce.oscposloFM[k] * (1<<24)); + int freqhiFM = vce.oscfreqhiFM[k]; + int freqloFM = (int)(vce.oscfreqloFM[k] * (1<<24)); + float *tw = tmpwave_unison[k]; + const float *smps = NoteVoicePar[nvoice].FMSmp; + + for(int i = 0; i < synth.buffersize; ++i) { + tw[i] = (smps[poshiFM] * ((1<<24) - posloFM) + + smps[poshiFM + 1] * posloFM) / (1.0f*(1<<24)); + if (FMmode == FMTYPE::PW_MOD && (k & 1)) + tw[i] = -tw[i]; + + posloFM += freqloFM; + if(posloFM >= (1<<24)) { + posloFM &= 0xffffff;//fmod(posloFM, 1.0f); + poshiFM++; + } + poshiFM += freqhiFM; + poshiFM &= synth.oscilsize - 1; + } + vce.oscposhiFM[k] = poshiFM; + vce.oscposloFM[k] = posloFM/((1<<24)*1.0f); + } + } + // Amplitude interpolation + if(ABOVE_AMPLITUDE_THRESHOLD(vce.FMoldamplitude, + vce.FMnewamplitude)) { + for(int k = 0; k < vce.unison_size; ++k) { + float *tw = tmpwave_unison[k]; + for(int i = 0; i < synth.buffersize; ++i) + tw[i] *= INTERPOLATE_AMPLITUDE(vce.FMoldamplitude, + vce.FMnewamplitude, + i, + synth.buffersize); + } + } else { + for(int k = 0; k < vce.unison_size; ++k) { + float *tw = tmpwave_unison[k]; + for(int i = 0; i < synth.buffersize; ++i) + tw[i] *= vce.FMnewamplitude; + } + } + + + //normalize: makes all sample-rates, oscil_sizes to produce same sound + if(FMmode == FMTYPE::FREQ_MOD) { //Frequency modulation + const float normalize = synth.oscilsize_f / 262144.0f * 44100.0f + / synth.samplerate_f; + for(int k = 0; k < vce.unison_size; ++k) { + float *tw = tmpwave_unison[k]; + float fmold = vce.FMoldsmp[k]; + for(int i = 0; i < synth.buffersize; ++i) { + fmold = fmodf(fmold + tw[i] * normalize, synth.oscilsize); + tw[i] = fmold; + } + vce.FMoldsmp[k] = fmold; + } + } + else { //Phase or PWM modulation + const float normalize = synth.oscilsize_f / 262144.0f; + for(int k = 0; k < vce.unison_size; ++k) { + float *tw = tmpwave_unison[k]; + for(int i = 0; i < synth.buffersize; ++i) + tw[i] *= normalize; + } + } + + //do the modulation + for(int k = 0; k < vce.unison_size; ++k) { + float *smps = NoteVoicePar[nvoice].OscilSmp; + float *tw = tmpwave_unison[k]; + int poshi = vce.oscposhi[k]; + int poslo = (int)(vce.oscposlo[k] * (1<<24)); + int freqhi = vce.oscfreqhi[k]; + int freqlo = (int)(vce.oscfreqlo[k] * (1<<24)); + + for(int i = 0; i < synth.buffersize; ++i) { + int FMmodfreqhi = 0; + F2I(tw[i], FMmodfreqhi); + float FMmodfreqlo = tw[i]-FMmodfreqhi;//fmod(tw[i] /*+ 0.0000000001f*/, 1.0f); + if(FMmodfreqhi < 0) + FMmodfreqlo++; + + //carrier + int carposhi = poshi + FMmodfreqhi; + int carposlo = (int)(poslo + FMmodfreqlo); + if (FMmode == FMTYPE::PW_MOD && (k & 1)) + carposhi += NoteVoicePar[nvoice].phase_offset; + + if(carposlo >= (1<<24)) { + carposhi++; + carposlo &= 0xffffff;//fmod(carposlo, 1.0f); + } + carposhi &= (synth.oscilsize - 1); + + tw[i] = (smps[carposhi] * ((1<<24) - carposlo) + + smps[carposhi + 1] * carposlo)/(1.0f*(1<<24)); + + poslo += freqlo; + if(poslo >= (1<<24)) { + poslo &= 0xffffff;//fmod(poslo, 1.0f); + poshi++; + } + + poshi += freqhi; + poshi &= synth.oscilsize - 1; + } + vce.oscposhi[k] = poshi; + vce.oscposlo[k] = (poslo)/((1<<24)*1.0f); + } +} + + +/* + * Computes the Noise + */ +inline void ADnote::ComputeVoiceWhiteNoise(int nvoice) +{ + for(int k = 0; k < NoteVoicePar[nvoice].unison_size; ++k) { + float *tw = tmpwave_unison[k]; + for(int i = 0; i < synth.buffersize; ++i) + tw[i] = RND * 2.0f - 1.0f; + } +} + +inline void ADnote::ComputeVoicePinkNoise(int nvoice) +{ + Voice& vce = NoteVoicePar[nvoice]; + for(int k = 0; k < vce.unison_size; ++k) { + float *tw = tmpwave_unison[k]; + float *f = &vce.pinking[k > 0 ? 7 : 0]; + for(int i = 0; i < synth.buffersize; ++i) { + float white = (RND-0.5f)/4.0f; + f[0] = 0.99886f*f[0]+white*0.0555179f; + f[1] = 0.99332f*f[1]+white*0.0750759f; + f[2] = 0.96900f*f[2]+white*0.1538520f; + f[3] = 0.86650f*f[3]+white*0.3104856f; + f[4] = 0.55000f*f[4]+white*0.5329522f; + f[5] = -0.7616f*f[5]-white*0.0168980f; + tw[i] = f[0]+f[1]+f[2]+f[3]+f[4]+f[5]+f[6]+white*0.5362f; + f[6] = white*0.115926f; + } + } +} + +inline void ADnote::ComputeVoiceDC(int nvoice) +{ + for(int k = 0; k < NoteVoicePar[nvoice].unison_size; ++k) { + float *tw = tmpwave_unison[k]; + for(int i = 0; i < synth.buffersize; ++i) + tw[i] = 1.0f; + } +} + + + +/* + * Compute the ADnote samples + * Returns 0 if the note is finished + */ +int ADnote::noteout(float *outl, float *outr) +{ + memcpy(outl, synth.denormalkillbuf, synth.bufferbytes); + memcpy(outr, synth.denormalkillbuf, synth.bufferbytes); + + if(NoteEnabled == OFF) + return 0; + + memset(bypassl, 0, synth.bufferbytes); + memset(bypassr, 0, synth.bufferbytes); + + //Update Changed Parameters From UI + for(unsigned nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + if((NoteVoicePar[nvoice].Enabled != ON) + || (NoteVoicePar[nvoice].DelayTicks > 0)) + continue; + setupVoiceDetune(nvoice); + setupVoiceMod(nvoice, false); + } + + computecurrentparameters(); + + for(unsigned nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + if((NoteVoicePar[nvoice].Enabled != ON) + || (NoteVoicePar[nvoice].DelayTicks > 0)) + continue; + switch (NoteVoicePar[nvoice].noisetype) { + case 0: //voice mode=sound + switch(NoteVoicePar[nvoice].FMEnabled) { + case FMTYPE::MIX: + ComputeVoiceOscillatorMix(nvoice); + break; + case FMTYPE::RING_MOD: + ComputeVoiceOscillatorRingModulation(nvoice); + break; + case FMTYPE::FREQ_MOD: + case FMTYPE::PHASE_MOD: + case FMTYPE::PW_MOD: + ComputeVoiceOscillatorFrequencyModulation(nvoice, + NoteVoicePar[nvoice].FMEnabled); + break; + default: + if(NoteVoicePar[nvoice].AAEnabled) ComputeVoiceOscillator_SincInterpolation(nvoice); + else ComputeVoiceOscillator_LinearInterpolation(nvoice); + //if (config.cfg.Interpolation) ComputeVoiceOscillator_CubicInterpolation(nvoice); + } + break; + case 1: + ComputeVoiceWhiteNoise(nvoice); + break; + case 2: + ComputeVoicePinkNoise(nvoice); + break; + default: + ComputeVoiceDC(nvoice); + break; + } + // Voice Processing + + Voice& vce = NoteVoicePar[nvoice]; + //mix subvoices into voice + memset(tmpwavel, 0, synth.bufferbytes); + if(stereo) + memset(tmpwaver, 0, synth.bufferbytes); + for(int k = 0; k < vce.unison_size; ++k) { + float *tw = tmpwave_unison[k]; + if(stereo) { + float stereo_pos = 0; + bool is_pwm = NoteVoicePar[nvoice].FMEnabled == FMTYPE::PW_MOD; + if (is_pwm) { + if(vce.unison_size > 2) + stereo_pos = k/2 + / (float)(vce.unison_size/2 + - 1) * 2.0f - 1.0f; + } else if(vce.unison_size > 1) { + stereo_pos = k + / (float)(vce.unison_size + - 1) * 2.0f - 1.0f; + } + float stereo_spread = vce.unison_stereo_spread * 2.0f; //between 0 and 2.0f + if(stereo_spread > 1.0f) { + float stereo_pos_1 = (stereo_pos >= 0.0f) ? 1.0f : -1.0f; + stereo_pos = + (2.0f + - stereo_spread) * stereo_pos + + (stereo_spread - 1.0f) * stereo_pos_1; + } + else + stereo_pos *= stereo_spread; + + if(vce.unison_size == 1 || + (is_pwm && vce.unison_size == 2)) + stereo_pos = 0.0f; + float panning = (stereo_pos + 1.0f) * 0.5f; + + + float lvol = (1.0f - panning) * 2.0f; + if(lvol > 1.0f) + lvol = 1.0f; + + float rvol = panning * 2.0f; + if(rvol > 1.0f) + rvol = 1.0f; + + if(vce.unison_invert_phase[k]) { + lvol = -lvol; + rvol = -rvol; + } + + for(int i = 0; i < synth.buffersize; ++i) + tmpwavel[i] += tw[i] * lvol; + for(int i = 0; i < synth.buffersize; ++i) + tmpwaver[i] += tw[i] * rvol; + } + else + for(int i = 0; i < synth.buffersize; ++i) + tmpwavel[i] += tw[i]; + if(nvoice == 0) + watch_be4_add(tmpwavel,synth.buffersize); + } + + float unison_amplitude = 1.0f / sqrtf(vce.unison_size); //reduce the amplitude for large unison sizes + // Amplitude + float oldam = vce.oldamplitude * unison_amplitude; + float newam = vce.newamplitude * unison_amplitude; + + if(ABOVE_AMPLITUDE_THRESHOLD(oldam, newam)) { + int rest = synth.buffersize; + //test if the amplitude if raising and the difference is high + if((newam > oldam) && ((newam - oldam) > 0.25f)) { + rest = 10; + if(rest > synth.buffersize) + rest = synth.buffersize; + for(int i = 0; i < synth.buffersize - rest; ++i) + tmpwavel[i] *= oldam; + if(stereo) + for(int i = 0; i < synth.buffersize - rest; ++i) + tmpwaver[i] *= oldam; + } + // Amplitude interpolation + for(int i = 0; i < rest; ++i) { + float amp = INTERPOLATE_AMPLITUDE(oldam, newam, i, rest); + tmpwavel[i + (synth.buffersize - rest)] *= amp; + if(stereo) + tmpwaver[i + (synth.buffersize - rest)] *= amp; + } + } + else { + for(int i = 0; i < synth.buffersize; ++i) + tmpwavel[i] *= newam; + if(stereo) + for(int i = 0; i < synth.buffersize; ++i) + tmpwaver[i] *= newam; + } + + // Fade in + if(vce.firsttick != 0) { + fadein(&tmpwavel[0]); + if(stereo) + fadein(&tmpwaver[0]); + vce.firsttick = 0; + } + + // Filter + if(NoteVoicePar[nvoice].Filter) { + if(stereo) + NoteVoicePar[nvoice].Filter->filter(tmpwavel, tmpwaver); + else + NoteVoicePar[nvoice].Filter->filter(tmpwavel, 0); + } + + //check if the amplitude envelope is finished, if yes, the voice will be fadeout + if(NoteVoicePar[nvoice].AmpEnvelope) + if(NoteVoicePar[nvoice].AmpEnvelope->finished()) { + for(int i = 0; i < synth.buffersize; ++i) + tmpwavel[i] *= 1.0f - (float)i / synth.buffersize_f; + if(stereo) + for(int i = 0; i < synth.buffersize; ++i) + tmpwaver[i] *= 1.0f - (float)i / synth.buffersize_f; + } + //the voice is killed later + + + // Put the ADnote samples in VoiceOut (without applying Global volume, because I wish to use this voice as a modullator) + if(NoteVoicePar[nvoice].VoiceOut) { + if(stereo) + for(int i = 0; i < synth.buffersize; ++i) + NoteVoicePar[nvoice].VoiceOut[i] = tmpwavel[i] + + tmpwaver[i]; + else //mono + for(int i = 0; i < synth.buffersize; ++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(int i = 0; i < synth.buffersize; ++i) { //stereo + outl[i] += tmpwavel[i] * NoteVoicePar[nvoice].Volume + * NoteVoicePar[nvoice].Panning * 2.0f; + outr[i] += tmpwaver[i] * NoteVoicePar[nvoice].Volume + * (1.0f - NoteVoicePar[nvoice].Panning) * 2.0f; + } + else + for(int i = 0; i < synth.buffersize; ++i) //mono + outl[i] += tmpwavel[i] * NoteVoicePar[nvoice].Volume; + } + else { //bypass the filter + if(stereo) + for(int i = 0; i < synth.buffersize; ++i) { //stereo + bypassl[i] += tmpwavel[i] * NoteVoicePar[nvoice].Volume + * NoteVoicePar[nvoice].Panning * 2.0f; + bypassr[i] += tmpwaver[i] * NoteVoicePar[nvoice].Volume + * (1.0f + - NoteVoicePar[nvoice].Panning) * 2.0f; + } + else + for(int i = 0; i < synth.buffersize; ++i) //mono + bypassl[i] += tmpwavel[i] * NoteVoicePar[nvoice].Volume; + } + // check if there is necessary to process the voice longer (if the Amplitude envelope isn't finished) + if(NoteVoicePar[nvoice].AmpEnvelope) + if(NoteVoicePar[nvoice].AmpEnvelope->finished()) + KillVoice(nvoice); + } + + //Processing Global parameters + if(stereo) { + NoteGlobalPar.Filter->filter(outl, outr); + } else { //set the right channel=left channel + NoteGlobalPar.Filter->filter(outl, 0); + memcpy(outr, outl, synth.bufferbytes); + memcpy(bypassr, bypassl, synth.bufferbytes); + } + + for(int i = 0; i < synth.buffersize; ++i) { + outl[i] += bypassl[i]; + outr[i] += bypassr[i]; + } + + if(ABOVE_AMPLITUDE_THRESHOLD(globaloldamplitude, globalnewamplitude)) + // Amplitude Interpolation + for(int i = 0; i < synth.buffersize; ++i) { + float tmpvol = INTERPOLATE_AMPLITUDE(globaloldamplitude, + globalnewamplitude, + i, + synth.buffersize); + outl[i] *= tmpvol * NoteGlobalPar.Panning; + outr[i] *= tmpvol * (1.0f - NoteGlobalPar.Panning); + } + else + for(int i = 0; i < synth.buffersize; ++i) { + outl[i] *= globalnewamplitude * NoteGlobalPar.Panning; + outr[i] *= globalnewamplitude * (1.0f - NoteGlobalPar.Panning); + } + + //Apply the punch + if(NoteGlobalPar.Punch.Enabled != 0) + for(int i = 0; i < synth.buffersize; ++i) { + float punchamp = NoteGlobalPar.Punch.initialvalue + * NoteGlobalPar.Punch.t + 1.0f; + outl[i] *= punchamp; + outr[i] *= punchamp; + NoteGlobalPar.Punch.t -= NoteGlobalPar.Punch.dt; + if(NoteGlobalPar.Punch.t < 0.0f) { + NoteGlobalPar.Punch.Enabled = 0; + break; + } + } + + watch_punch(outl, synth.buffersize); + watch_after_add(outl,synth.buffersize); + + // Apply legato-specific sound signal modifications + legato.apply(*this, outl, outr); + + watch_legato(outl, synth.buffersize); + + // Check if the global amplitude is finished. + // If it does, disable the note + if(NoteGlobalPar.AmpEnvelope->finished()) { + for(int i = 0; i < synth.buffersize; ++i) { //fade-out + float tmp = 1.0f - (float)i / synth.buffersize_f; + outl[i] *= tmp; + outr[i] *= tmp; + } + KillNote(); + } + return 1; +} + + +/* + * Release the key (NoteOff) + */ +void ADnote::releasekey() +{ + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) + NoteVoicePar[nvoice].releasekey(); + NoteGlobalPar.FreqEnvelope->releasekey(); + NoteGlobalPar.FilterEnvelope->releasekey(); + NoteGlobalPar.AmpEnvelope->releasekey(); +} + +/* + * Check if the note is finished + */ +bool ADnote::finished() const +{ + if(NoteEnabled == ON) + return 0; + else + return 1; +} + +void ADnote::entomb(void) +{ + NoteGlobalPar.AmpEnvelope->forceFinish(); +} + +void ADnote::Voice::releasekey() +{ + if(!Enabled) + return; + if(AmpEnvelope) + AmpEnvelope->releasekey(); + if(FreqEnvelope) + FreqEnvelope->releasekey(); + if(FilterEnvelope) + FilterEnvelope->releasekey(); + if(FMFreqEnvelope) + FMFreqEnvelope->releasekey(); + if(FMAmpEnvelope) + FMAmpEnvelope->releasekey(); +} + +void ADnote::Voice::kill(Allocator &memory, const SYNTH_T &synth) +{ + memory.devalloc(OscilSmp); + memory.dealloc(FreqEnvelope); + memory.dealloc(FreqLfo); + memory.dealloc(AmpEnvelope); + memory.dealloc(AmpLfo); + memory.dealloc(Filter); + memory.dealloc(FilterEnvelope); + memory.dealloc(FilterLfo); + memory.dealloc(FMFreqEnvelope); + memory.dealloc(FMAmpEnvelope); + + if((FMEnabled != FMTYPE::NONE) && (FMVoice < 0)) + memory.devalloc(FMSmp); + + if(VoiceOut) + memset(VoiceOut, 0, synth.bufferbytes); + //the buffer can't be safely deleted as it may be + //an input to another voice + + Enabled = OFF; +} + +void ADnote::Global::kill(Allocator &memory) +{ + memory.dealloc(FreqEnvelope); + memory.dealloc(FreqLfo); + memory.dealloc(AmpEnvelope); + memory.dealloc(AmpLfo); + memory.dealloc(Filter); + memory.dealloc(FilterEnvelope); + memory.dealloc(FilterLfo); +} + +void ADnote::Global::initparameters(const ADnoteGlobalParam &param, + const SYNTH_T &synth, + const AbsTime &time, + class Allocator &memory, + float basefreq, float velocity, + bool stereo, + WatchManager *wm, + const char *prefix) +{ + ScratchString pre = prefix; + FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, basefreq, + synth.dt(), wm, (pre+"GlobalPar/FreqEnvelope/").c_str); + FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time, wm, + (pre+"GlobalPar/FreqLfo/").c_str); + + AmpEnvelope = memory.alloc<Envelope>(*param.AmpEnvelope, basefreq, + synth.dt(), wm, (pre+"GlobalPar/AmpEnvelope/").c_str); + AmpLfo = memory.alloc<LFO>(*param.AmpLfo, basefreq, time, wm, + (pre+"GlobalPar/AmpLfo/").c_str); + + Volume = dB2rap(param.Volume) + * VelF(velocity, param.PAmpVelocityScaleFunction); //sensing + + Filter = memory.alloc<ModFilter>(*param.GlobalFilter, synth, time, memory, + stereo, basefreq); + + FilterEnvelope = memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, + synth.dt(), wm, (pre+"GlobalPar/FilterEnvelope/").c_str); + FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time, wm, + (pre+"GlobalPar/FilterLfo/").c_str); + + Filter->addMod(*FilterEnvelope); + Filter->addMod(*FilterLfo); + + { + Filter->updateSense(velocity, param.PFilterVelocityScale, + param.PFilterVelocityScaleFunction); + } +} + +}