commit 3b06d5ced5d0ea69ab35a8cafe4e7474c0c13a2b
parent 5c938b79f7353432fe473c8841b80a446789b0b4
Author: Friedolino <[email protected]>
Date: Wed, 22 Apr 2020 12:09:15 +0200
adsynth more comments and small fixes
Diffstat:
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 ¶m = 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 ¶m = 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 ¶m = 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 ¶m,
+ 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);
+ }
+}
+
+}