From 2ff16339e3359f7142b281cc5839b3927852349f Mon Sep 17 00:00:00 2001 From: Mario Kruselj Date: Thu, 19 Nov 2020 15:47:38 +0100 Subject: [PATCH 1/3] Implement retrigger properly for all oscillator types Closes #3171 --- src/common/SurgeStorage.h | 18 +-- src/common/dsp/FM2Oscillator.cpp | 34 ++--- src/common/dsp/FM2Oscillator.h | 11 ++ src/common/dsp/FM3Oscillator.cpp | 48 +++---- src/common/dsp/FM3Oscillator.h | 15 +- src/common/dsp/SampleAndHoldOscillator.cpp | 3 +- src/common/dsp/SineOscillator.cpp | 157 +++++++++++---------- src/common/dsp/SineOscillator.h | 2 +- src/common/dsp/SurgeSuperOscillator.cpp | 22 +-- src/common/dsp/SurgeSuperOscillator.h | 11 ++ src/common/dsp/WindowOscillator.cpp | 21 ++- 11 files changed, 178 insertions(+), 164 deletions(-) diff --git a/src/common/SurgeStorage.h b/src/common/SurgeStorage.h index 72d8831c89e..1a5454c5f0c 100644 --- a/src/common/SurgeStorage.h +++ b/src/common/SurgeStorage.h @@ -65,19 +65,15 @@ const int n_fx_slots=8; // 2 -> 3 filter subtypes added comb should default to 1 and moog to 3 // 3 -> 4 comb+/- combined into 1 filtertype (subtype 0,0->0 0,1->1 1,0->2 1,1->3 ) // 4 -> 5 stereo filterconf now have seperate pan controls -// 5 -> 6 new filter sound in v1.2 (same parameters, but different sound & changed resonance -// response). +// 5 -> 6 new filter sound in v1.2 (same parameters, but different sound & changed resonance response) // 6 -> 7 custom controller state now stored (in seq. recall) -// 7 -> 8 larger resonance -// range (old filters are set to subtype 1), pan2 -> width -// 8 -> 9 now 8 controls (offset ids larger -// than ctrl7 by +1), custom controllers have names (guess for pre-rev9 patches) +// 7 -> 8 larger resonance range (old filters are set to subtype 1), pan2 -> width +// 8 -> 9 now 8 macros (offset IDs larger than ctrl7 by +1), macros have names (guess for pre-rev9 patches) // 9 -> 10 added character parameter -// 10 -> 11 (1.6.2 release) added DAW Extra State -// 11 -> 12 (1.6.3 release) added new parameters to the Distortion effect -// 12 -> 13 (1.7.0 release) deactivation; sine LP/HP, sine/FM2/3 feedback extension/bipolar -// 13 -> 14 add phaser number of stages parameter -// 13 -> 14 add ability to configure vocoder modulator mono/sterao/L/R +// 10 -> 11 (1.6.2 release) added DAW extra state +// 11 -> 12 (1.6.3 release) added new parameters to Distortion effect +// 12 -> 13 (1.7.0 release) parameter deactivation; sine LP/HP, sine/FM2/3 feedback extension/bipolar +// 13 -> 14 (1.8.0 release) add phaser stages parameter, add vocoder modulator input channel parameter, osc retrigger actually works for window/sine/FM2/FM3 const int ff_revision = 14; diff --git a/src/common/dsp/FM2Oscillator.cpp b/src/common/dsp/FM2Oscillator.cpp index b4046d1f53e..1d0c993fa5b 100644 --- a/src/common/dsp/FM2Oscillator.cpp +++ b/src/common/dsp/FM2Oscillator.cpp @@ -19,17 +19,6 @@ using std::min; using std::max; -enum fm2_params -{ - fm2_m1amount = 0, - fm2_m1ratio, - fm2_m2amount, - fm2_m2ratio, - fm2_m12offset, - fm2_m12phase, - fm2_feedback, -}; - FM2Oscillator::FM2Oscillator(SurgeStorage* storage, OscillatorStorage* oscdata, pdata* localcopy) : Oscillator(storage, oscdata, localcopy) {} @@ -41,17 +30,18 @@ double calcmd(double x) void FM2Oscillator::init(float pitch, bool is_display) { - // phase = oscdata->retrigger.val.b ? ((oscdata->startphase.val.f) * M_PI * 2) : 0.f; + if (is_display) + phase = 0.f; + else + phase = oscdata->retrigger.val.b ? 0.f : (2.0 * M_PI * rand() / RAND_MAX - M_PI); lastoutput = 0.0; driftlfo = 0; driftlfo2 = 0; fb_val = 0.0; - double ph = localcopy[oscdata->p[fm2_m12phase].param_id_in_scene].f * 2.0 * M_PI; + double ph = (localcopy[oscdata->p[fm2_m12phase].param_id_in_scene].f + phase) * 2.0 * M_PI; RM1.set_phase(ph); RM2.set_phase(ph); - phase = -sin(ph) * (calcmd(localcopy[oscdata->p[fm2_m1amount].param_id_in_scene].f) + - calcmd(localcopy[oscdata->p[fm2_m2amount].param_id_in_scene].f)) - - ph; + phase = -sin(ph) * (calcmd(localcopy[oscdata->p[fm2_m1amount].param_id_in_scene].f) + calcmd(localcopy[oscdata->p[fm2_m2amount].param_id_in_scene].f)) - ph; } FM2Oscillator::~FM2Oscillator() @@ -65,10 +55,8 @@ void FM2Oscillator::process_block(float pitch, float drift, bool stereo, bool FM double omega = min(M_PI, (double)pitch_to_omega(pitch + driftlfo)); double shift = localcopy[oscdata->p[fm2_m12offset].param_id_in_scene].f * dsamplerate_inv; - RM1.set_rate(min(M_PI, (double)pitch_to_omega(pitch + driftlfo) * - (double)localcopy[oscdata->p[fm2_m1ratio].param_id_in_scene].i + shift)); - RM2.set_rate(min(M_PI, (double)pitch_to_omega(pitch + driftlfo) * - (double)localcopy[oscdata->p[fm2_m2ratio].param_id_in_scene].i - shift)); + RM1.set_rate(min(M_PI, (double)pitch_to_omega(pitch + driftlfo) * (double)localcopy[oscdata->p[fm2_m1ratio].param_id_in_scene].i + shift)); + RM2.set_rate(min(M_PI, (double)pitch_to_omega(pitch + driftlfo) * (double)localcopy[oscdata->p[fm2_m2ratio].param_id_in_scene].i - shift)); double d1 = localcopy[oscdata->p[fm2_m1amount].param_id_in_scene].f; double d2 = localcopy[oscdata->p[fm2_m2amount].param_id_in_scene].f; @@ -143,4 +131,8 @@ void FM2Oscillator::handleStreamingMismatches(int streamingRevision, int current { oscdata->p[fm2_feedback].set_type(ct_osc_feedback); } -} + if (streamingRevision < 14) + { + oscdata->retrigger.val.b = true; + } +} \ No newline at end of file diff --git a/src/common/dsp/FM2Oscillator.h b/src/common/dsp/FM2Oscillator.h index 031ab272e20..b28a4280856 100644 --- a/src/common/dsp/FM2Oscillator.h +++ b/src/common/dsp/FM2Oscillator.h @@ -23,6 +23,17 @@ class FM2Oscillator : public Oscillator { public: + enum fm2_params + { + fm2_m1amount = 0, + fm2_m1ratio, + fm2_m2amount, + fm2_m2ratio, + fm2_m12offset, + fm2_m12phase, + fm2_feedback, + }; + FM2Oscillator(SurgeStorage* storage, OscillatorStorage* oscdata, pdata* localcopy); virtual void init(float pitch, bool is_display = false) override; virtual void process_block( diff --git a/src/common/dsp/FM3Oscillator.cpp b/src/common/dsp/FM3Oscillator.cpp index 011421dd47f..f9dd9cd19eb 100644 --- a/src/common/dsp/FM3Oscillator.cpp +++ b/src/common/dsp/FM3Oscillator.cpp @@ -19,34 +19,23 @@ using namespace std; -/* FM osc */ - -enum fm3_params -{ - fm3_m1amount = 0, - fm3_m1ratio, - fm3_m2amount, - fm3_m2ratio, - fm3_m3amount, - fm3_m3freq, - fm3_feedback, -}; - FM3Oscillator::FM3Oscillator(SurgeStorage* storage, OscillatorStorage* oscdata, pdata* localcopy) : Oscillator(storage, oscdata, localcopy) {} void FM3Oscillator::init(float pitch, bool is_display) { - // phase = oscdata->retrigger.val.b ? ((oscdata->startphase.val.f) * M_PI * 2) : 0.f; - phase = 0.0; + if (is_display) + phase = 0.0; + else + phase = oscdata->retrigger.val.b ? 0.f : (2.0 * M_PI * rand() / RAND_MAX - M_PI); lastoutput = 0.0; driftlfo = 0; driftlfo2 = 0; fb_val = 0.0; - AM.set_phase(0.0); - RM1.set_phase(0.0); - RM2.set_phase(0.0); + AM.set_phase(phase); + RM1.set_phase(phase); + RM2.set_phase(phase); } FM3Oscillator::~FM3Oscillator() @@ -60,14 +49,15 @@ void FM3Oscillator::process_block(float pitch, float drift, bool stereo, bool FM double omega = min(M_PI, (double)pitch_to_omega(pitch + driftlfo)); auto m1 = oscdata->p[fm3_m1ratio].get_extended(localcopy[oscdata->p[fm3_m1ratio].param_id_in_scene].f); - if( m1 < 0 ) m1 = 1.0 / m1; - RM1.set_rate(min(M_PI, (double)pitch_to_omega(pitch + driftlfo) * m1 ) ); + if (m1 < 0) + m1 = 1.0 / m1; + RM1.set_rate(min(M_PI, (double)pitch_to_omega(pitch + driftlfo) * m1)); auto m2 = oscdata->p[fm3_m2ratio].get_extended(localcopy[oscdata->p[fm3_m2ratio].param_id_in_scene].f); - if( m2 < 0 ) m2 = 1.0 / m2; - RM2.set_rate(min(M_PI, (double)pitch_to_omega(pitch + driftlfo) * m2 ) ); - AM.set_rate(min( - M_PI, (double)pitch_to_omega(60.0 + localcopy[oscdata->p[fm3_m3freq].param_id_in_scene].f))); + if (m2 < 0) + m2 = 1.0 / m2; + RM2.set_rate(min(M_PI, (double)pitch_to_omega(pitch + driftlfo) * m2)); + AM.set_rate(min(M_PI, (double)pitch_to_omega(60.0 + localcopy[oscdata->p[fm3_m3freq].param_id_in_scene].f))); double d1 = localcopy[oscdata->p[fm3_m1amount].param_id_in_scene].f; double d2 = localcopy[oscdata->p[fm3_m2amount].param_id_in_scene].f; @@ -88,8 +78,7 @@ void FM3Oscillator::process_block(float pitch, float drift, bool stereo, bool FM RM2.process(); AM.process(); - output[k] = phase + RelModDepth1.v * RM1.r + RelModDepth2.v * RM2.r + AbsModDepth.v * AM.r + - lastoutput; + output[k] = phase + RelModDepth1.v * RM1.r + RelModDepth2.v * RM2.r + AbsModDepth.v * AM.r + lastoutput; if (FM) output[k] += FMdepth.v * master_osc[k]; @@ -151,5 +140,8 @@ void FM3Oscillator::handleStreamingMismatches(int streamingRevision, int current { oscdata->p[fm3_feedback].set_type(ct_osc_feedback); } -} - + if (streamingRevision < 14) + { + oscdata->retrigger.val.b = true; + } +} \ No newline at end of file diff --git a/src/common/dsp/FM3Oscillator.h b/src/common/dsp/FM3Oscillator.h index e37ab4a336e..e97c44bb5fe 100644 --- a/src/common/dsp/FM3Oscillator.h +++ b/src/common/dsp/FM3Oscillator.h @@ -23,6 +23,17 @@ class FM3Oscillator : public Oscillator { public: + enum fm3_params + { + fm3_m1amount = 0, + fm3_m1ratio, + fm3_m2amount, + fm3_m2ratio, + fm3_m3amount, + fm3_m3freq, + fm3_feedback, + }; + FM3Oscillator(SurgeStorage* storage, OscillatorStorage* oscdata, pdata* localcopy); virtual void init(float pitch, bool is_display = false) override; virtual void process_block( @@ -36,6 +47,4 @@ class FM3Oscillator : public Oscillator float fb_val; lag FMdepth, AbsModDepth, RelModDepth1, RelModDepth2, FeedbackDepth; virtual void handleStreamingMismatches(int streamingRevision, int currentSynthStreamingRevision) override; -}; - - +}; \ No newline at end of file diff --git a/src/common/dsp/SampleAndHoldOscillator.cpp b/src/common/dsp/SampleAndHoldOscillator.cpp index c3b3ee5bc0b..413ac2abbc2 100644 --- a/src/common/dsp/SampleAndHoldOscillator.cpp +++ b/src/common/dsp/SampleAndHoldOscillator.cpp @@ -112,8 +112,7 @@ void SampleAndHoldOscillator::init(float pitch, bool is_display) else { double drand = (double)rand() / RAND_MAX; - double detune = oscdata->p[shn_unison_detune].get_extended(localcopy[id_detune].f) * - (detune_bias * float(i) + detune_offset); + double detune = oscdata->p[shn_unison_detune].get_extended(localcopy[id_detune].f) * (detune_bias * float(i) + detune_offset); double st = drand * storage->note_to_pitch_tuningctr(detune) * 0.5; drand = (double)rand() / RAND_MAX; double ot = drand * storage->note_to_pitch_tuningctr(detune); diff --git a/src/common/dsp/SineOscillator.cpp b/src/common/dsp/SineOscillator.cpp index 9884c5632c6..14ca4a56106 100644 --- a/src/common/dsp/SineOscillator.cpp +++ b/src/common/dsp/SineOscillator.cpp @@ -19,13 +19,15 @@ enum sine_params { - sin_shape, - sin_feedback, - sin_FMmode, - sin_lowcut, - sin_highcut, - sin_unison_detune, - sin_unison_voices, + sine_shape, + sine_feedback, + sine_FMmode, + sine_lowcut, + sine_highcut, + sine_unison_detune, + sine_unison_voices, + + num_sine_params, }; SineOscillator::SineOscillator(SurgeStorage* storage, OscillatorStorage* oscdata, pdata* localcopy) @@ -73,35 +75,39 @@ void SineOscillator::prepare_unison(int voices) void SineOscillator::init(float pitch, bool is_display) { - n_unison = limit_range(oscdata->p[sin_unison_voices].val.i, 1, MAX_UNISON); + n_unison = limit_range(oscdata->p[sine_unison_voices].val.i, 1, MAX_UNISON); if (is_display) n_unison = 1; prepare_unison(n_unison); for (int i = 0; i < n_unison; i++) { - if (i > 0) - phase[i] = 2.0 * M_PI * rand() / RAND_MAX - M_PI; // phase in range -PI to PI - else + if (oscdata->retrigger.val.b || is_display) + { phase[i] = 0.f; - lastvalue[i] = 0.f; + } + else + { + phase[i] = 2.0 * M_PI * rand() / RAND_MAX - M_PI; // phase in range -PI to PI + } driftlfo[i] = 0.f; driftlfo2[i] = 0.f; - sinus[i].set_phase(phase[i]); + lastvalue[i] = 0.f; + sine[i].set_phase(phase[i]); } fb_val = 0.f; - id_mode = oscdata->p[sin_shape].param_id_in_scene; - id_fb = oscdata->p[sin_feedback].param_id_in_scene; - id_fmlegacy = oscdata->p[sin_FMmode].param_id_in_scene; - id_detune = oscdata->p[sin_unison_detune].param_id_in_scene; + id_mode = oscdata->p[sine_shape].param_id_in_scene; + id_fb = oscdata->p[sine_feedback].param_id_in_scene; + id_fmlegacy = oscdata->p[sine_FMmode].param_id_in_scene; + id_detune = oscdata->p[sine_unison_detune].param_id_in_scene; hp.coeff_instantize(); lp.coeff_instantize(); - hp.coeff_HP(hp.calc_omega(oscdata->p[sin_lowcut].val.f / 12.0) / OSC_OVERSAMPLING, 0.707); - lp.coeff_LP2B(lp.calc_omega(oscdata->p[sin_highcut].val.f / 12.0) / OSC_OVERSAMPLING, 0.707); + hp.coeff_HP(hp.calc_omega(oscdata->p[sine_lowcut].val.f / 12.0) / OSC_OVERSAMPLING, 0.707); + lp.coeff_LP2B(lp.calc_omega(oscdata->p[sine_highcut].val.f / 12.0) / OSC_OVERSAMPLING, 0.707); } SineOscillator::~SineOscillator() @@ -116,7 +122,7 @@ void SineOscillator::process_block(float pitch, float drift, bool stereo, bool F return; } - fb_val = oscdata->p[sin_feedback].get_extended(localcopy[id_fb].f); + fb_val = oscdata->p[sine_feedback].get_extended(localcopy[id_fb].f); double detune; double omega[MAX_UNISON]; @@ -128,14 +134,14 @@ void SineOscillator::process_block(float pitch, float drift, bool stereo, bool F if (n_unison > 1) { - if (oscdata->p[sin_unison_detune].absolute) + if (oscdata->p[sine_unison_detune].absolute) { - detune += oscdata->p[sin_unison_detune].get_extended(localcopy[oscdata->p[sin_unison_detune].param_id_in_scene].f) * + detune += oscdata->p[sine_unison_detune].get_extended(localcopy[oscdata->p[sine_unison_detune].param_id_in_scene].f) * storage->note_to_pitch_inv_ignoring_tuning(std::min(148.f, pitch)) * 16 / 0.9443 * (detune_bias * float(l) + detune_offset); } else { - detune += oscdata->p[sin_unison_detune].get_extended(localcopy[id_detune].f) * (detune_bias * float(l) + detune_offset); + detune += oscdata->p[sine_unison_detune].get_extended(localcopy[id_detune].f) * (detune_bias * float(l) + detune_offset); } } @@ -205,16 +211,16 @@ void SineOscillator::process_block(float pitch, float drift, bool stereo, bool F void SineOscillator::applyFilter() { - if (!oscdata->p[sin_lowcut].deactivated) - hp.coeff_HP(hp.calc_omega(localcopy[oscdata->p[sin_lowcut].param_id_in_scene].f / 12.0) / OSC_OVERSAMPLING, 0.707); - if (!oscdata->p[sin_highcut].deactivated) - lp.coeff_LP2B(lp.calc_omega(localcopy[oscdata->p[sin_highcut].param_id_in_scene].f / 12.0) / OSC_OVERSAMPLING, 0.707); + if (!oscdata->p[sine_lowcut].deactivated) + hp.coeff_HP(hp.calc_omega(localcopy[oscdata->p[sine_lowcut].param_id_in_scene].f / 12.0) / OSC_OVERSAMPLING, 0.707); + if (!oscdata->p[sine_highcut].deactivated) + lp.coeff_LP2B(lp.calc_omega(localcopy[oscdata->p[sine_highcut].param_id_in_scene].f / 12.0) / OSC_OVERSAMPLING, 0.707); for (int k = 0; k < BLOCK_SIZE_OS; k += BLOCK_SIZE) { - if (!oscdata->p[sin_lowcut].deactivated) + if (!oscdata->p[sine_lowcut].deactivated) hp.process_block(&(output[k]), &(outputR[k])); - if (!oscdata->p[sin_highcut].deactivated) + if (!oscdata->p[sine_highcut].deactivated) lp.process_block(&(output[k]), &(outputR[k])); } } @@ -234,14 +240,14 @@ void SineOscillator::process_block_legacy( if (n_unison > 1) { - if (oscdata->p[sin_unison_detune].absolute) + if (oscdata->p[sine_unison_detune].absolute) { - detune += oscdata->p[sin_unison_detune].get_extended(localcopy[oscdata->p[sin_unison_detune].param_id_in_scene].f) * + detune += oscdata->p[sine_unison_detune].get_extended(localcopy[oscdata->p[sine_unison_detune].param_id_in_scene].f) * storage->note_to_pitch_inv_ignoring_tuning(std::min(148.f, pitch)) * 16 / 0.9443 * (detune_bias * float(l) + detune_offset); } else { - detune += oscdata->p[sin_unison_detune].get_extended(localcopy[id_detune].f) * (detune_bias * float(l) + detune_offset); + detune += oscdata->p[sine_unison_detune].get_extended(localcopy[id_detune].f) * (detune_bias * float(l) + detune_offset); } } @@ -256,8 +262,7 @@ void SineOscillator::process_block_legacy( for (int u = 0; u < n_unison; u++) { - float out_local = - valueFromSinAndCos(Surge::DSP::fastsin(phase[u]), Surge::DSP::fastcos(phase[u])); + float out_local = valueFromSinAndCos(Surge::DSP::fastsin(phase[u]), Surge::DSP::fastcos(phase[u])); outL += (panL[u] * out_local) * out_attenuation * playingramp[u]; outR += (panR[u] * out_local) * out_attenuation * playingramp[u]; @@ -291,10 +296,10 @@ void SineOscillator::process_block_legacy( detune = drift * driftlfo[l]; if (n_unison > 1) - detune += oscdata->p[sin_unison_detune].get_extended(localcopy[id_detune].f) * (detune_bias * float(l) + detune_offset); + detune += oscdata->p[sine_unison_detune].get_extended(localcopy[id_detune].f) * (detune_bias * float(l) + detune_offset); omega[l] = std::min(M_PI, (double)pitch_to_omega(pitch + detune)); - sinus[l].set_rate(omega[l]); + sine[l].set_rate(omega[l]); } for (int k = 0; k < BLOCK_SIZE_OS; k++) @@ -303,10 +308,10 @@ void SineOscillator::process_block_legacy( for (int u = 0; u < n_unison; u++) { - sinus[u].process(); + sine[u].process(); - float sinx = sinus[u].r; - float cosx = sinus[u].i; + float sinx = sine[u].r; + float cosx = sine[u].i; float out_local = valueFromSinAndCos(sinx, cosx); @@ -619,69 +624,73 @@ void SineOscillator::handleStreamingMismatches(int streamingRevision, { if (streamingRevision <= 9) { - oscdata->p[sin_shape].val.i = oscdata->p[sin_shape].val_min.i; + oscdata->p[sine_shape].val.i = oscdata->p[sine_shape].val_min.i; } if (streamingRevision <= 10) { - oscdata->p[sin_feedback].val.f = 0; - oscdata->p[sin_FMmode].val.i = 0; + oscdata->p[sine_feedback].val.f = 0; + oscdata->p[sine_FMmode].val.i = 0; } if (streamingRevision <= 12) { - oscdata->p[sin_lowcut].val.f = oscdata->p[sin_lowcut].val_min.f; // high cut at the bottom - oscdata->p[sin_lowcut].deactivated = true; - oscdata->p[sin_highcut].val.f = oscdata->p[sin_highcut].val_max.f; // low cut at the top - oscdata->p[sin_highcut].deactivated = true; - oscdata->p[sin_feedback].set_type(ct_osc_feedback); + oscdata->p[sine_lowcut].val.f = oscdata->p[sine_lowcut].val_min.f; // high cut at the bottom + oscdata->p[sine_lowcut].deactivated = true; + oscdata->p[sine_highcut].val.f = oscdata->p[sine_highcut].val_max.f; // low cut at the top + oscdata->p[sine_highcut].deactivated = true; + oscdata->p[sine_feedback].set_type(ct_osc_feedback); int wave_remap[] = {0, 8, 9, 10, 1, 11, 4, 12, 13, 2, 3, 5, 6, 7, 14, 15, 16, 17, 18, 19}; // range checking for garbage data - if (oscdata->p[sin_shape].val.i < 0 || (oscdata->p[sin_shape].val.i >= (sizeof wave_remap) / sizeof *wave_remap)) - oscdata->p[sin_shape].val.i = oscdata->p[sin_shape].val_min.i; + if (oscdata->p[sine_shape].val.i < 0 || (oscdata->p[sine_shape].val.i >= (sizeof wave_remap) / sizeof *wave_remap)) + oscdata->p[sine_shape].val.i = oscdata->p[sine_shape].val_min.i; else { // make sure old patches still point to the correct waveforms - oscdata->p[sin_shape].val.i = wave_remap[oscdata->p[sin_shape].val.i]; + oscdata->p[sine_shape].val.i = wave_remap[oscdata->p[sine_shape].val.i]; } } + if (streamingRevision < 14) + { + oscdata->retrigger.val.b = true; + } } void SineOscillator::init_ctrltypes() { - oscdata->p[sin_shape].set_name("Shape"); - oscdata->p[sin_shape].set_type(ct_sineoscmode); + oscdata->p[sine_shape].set_name("Shape"); + oscdata->p[sine_shape].set_type(ct_sineoscmode); - oscdata->p[sin_feedback].set_name("Feedback"); - oscdata->p[sin_feedback].set_type(ct_osc_feedback_negative); + oscdata->p[sine_feedback].set_name("Feedback"); + oscdata->p[sine_feedback].set_type(ct_osc_feedback_negative); - oscdata->p[sin_FMmode].set_name("FM Behaviour"); - oscdata->p[sin_FMmode].set_type(ct_sinefmlegacy); + oscdata->p[sine_FMmode].set_name("FM Behaviour"); + oscdata->p[sine_FMmode].set_type(ct_sinefmlegacy); - oscdata->p[sin_lowcut].set_name("Low Cut"); - oscdata->p[sin_lowcut].set_type(ct_freq_audible_deactivatable); + oscdata->p[sine_lowcut].set_name("Low Cut"); + oscdata->p[sine_lowcut].set_type(ct_freq_audible_deactivatable); - oscdata->p[sin_highcut].set_name("High Cut"); - oscdata->p[sin_highcut].set_type(ct_freq_audible_deactivatable); + oscdata->p[sine_highcut].set_name("High Cut"); + oscdata->p[sine_highcut].set_type(ct_freq_audible_deactivatable); - oscdata->p[sin_unison_detune].set_name("Unison Detune"); - oscdata->p[sin_unison_detune].set_type(ct_oscspread); + oscdata->p[sine_unison_detune].set_name("Unison Detune"); + oscdata->p[sine_unison_detune].set_type(ct_oscspread); - oscdata->p[sin_unison_voices].set_name("Unison Voices"); - oscdata->p[sin_unison_voices].set_type(ct_osccount); + oscdata->p[sine_unison_voices].set_name("Unison Voices"); + oscdata->p[sine_unison_voices].set_type(ct_osccount); } void SineOscillator::init_default_values() { - oscdata->p[sin_shape].val.i = 0; - oscdata->p[sin_feedback].val.f = 0; - oscdata->p[sin_FMmode].val.i = 1; - - oscdata->p[sin_lowcut].val.f = oscdata->p[sin_lowcut].val_min.f; // high cut at the bottom - oscdata->p[sin_lowcut].deactivated = true; - oscdata->p[sin_highcut].val.f = oscdata->p[sin_highcut].val_max.f; // low cut at the top - oscdata->p[sin_highcut].deactivated = true; + oscdata->p[sine_shape].val.i = 0; + oscdata->p[sine_feedback].val.f = 0; + oscdata->p[sine_FMmode].val.i = 1; + + oscdata->p[sine_lowcut].val.f = oscdata->p[sine_lowcut].val_min.f; // high cut at the bottom + oscdata->p[sine_lowcut].deactivated = true; + oscdata->p[sine_highcut].val.f = oscdata->p[sine_highcut].val_max.f; // low cut at the top + oscdata->p[sine_highcut].deactivated = true; - oscdata->p[sin_unison_detune].val.f = 0.2; - oscdata->p[sin_unison_voices].val.i = 1; + oscdata->p[sine_unison_detune].val.f = 0.2; + oscdata->p[sine_unison_voices].val.i = 1; } diff --git a/src/common/dsp/SineOscillator.h b/src/common/dsp/SineOscillator.h index 4d165eafa68..5dddec550bb 100644 --- a/src/common/dsp/SineOscillator.h +++ b/src/common/dsp/SineOscillator.h @@ -33,7 +33,7 @@ class SineOscillator : public Oscillator virtual void init_ctrltypes() override; virtual void init_default_values() override; - quadr_osc sinus[MAX_UNISON]; + quadr_osc sine[MAX_UNISON]; double phase[MAX_UNISON]; float driftlfo[MAX_UNISON], driftlfo2[MAX_UNISON]; float fb_val; diff --git a/src/common/dsp/SurgeSuperOscillator.cpp b/src/common/dsp/SurgeSuperOscillator.cpp index 1917a8242f8..49c8e024110 100644 --- a/src/common/dsp/SurgeSuperOscillator.cpp +++ b/src/common/dsp/SurgeSuperOscillator.cpp @@ -145,18 +145,8 @@ using namespace std; // 202 samples (American) // const float integrator_hpf = 0.999f; // pow(ln(0.5)/(samplerate/50hz) -const float hpf_cycle_loss = 0.995f; -enum sso_params -{ - sso_shape = 0, - sso_width1, - sso_width2, - sso_mainsubmix, - sso_sync, - sso_unison_detune, - sso_unison_voices, -}; +const float hpf_cycle_loss = 0.995f; AbstractBlitOscillator::AbstractBlitOscillator(SurgeStorage* storage, OscillatorStorage* oscdata, @@ -285,24 +275,22 @@ void SurgeSuperOscillator::init(float pitch, bool is_display) { oscstate[i] = 0.f; syncstate[i] = 0.f; - last_level[i] = 0.f; } else { double drand = (double)rand() / RAND_MAX; - double detune = oscdata->p[sso_unison_detune].get_extended(localcopy[id_detune].f) * - (detune_bias * float(i) + detune_offset); + double detune = oscdata->p[sso_unison_detune].get_extended(localcopy[id_detune].f) * (detune_bias * float(i) + detune_offset); double st = 0.5 * drand * storage->note_to_pitch_inv_tuningctr(detune); drand = (double)rand() / RAND_MAX; oscstate[i] = st; syncstate[i] = st; - last_level[i] = 0.0; } dc_uni[i] = 0.f; - state[i] = 0.f; - pwidth[i] = limit_range(l_pw.v, 0.001f, 0.999f); driftlfo[i] = 0.f; driftlfo2[i] = 0.f; + last_level[i] = 0.f; + state[i] = 0.f; + pwidth[i] = limit_range(l_pw.v, 0.001f, 0.999f); } } diff --git a/src/common/dsp/SurgeSuperOscillator.h b/src/common/dsp/SurgeSuperOscillator.h index 47a508469f1..e53d8ad4bb5 100644 --- a/src/common/dsp/SurgeSuperOscillator.h +++ b/src/common/dsp/SurgeSuperOscillator.h @@ -27,6 +27,17 @@ class SurgeSuperOscillator : public AbstractBlitOscillator float FMphase alignas(16)[BLOCK_SIZE_OS + 4]; public: + enum sso_params + { + sso_shape = 0, + sso_width1, + sso_width2, + sso_mainsubmix, + sso_sync, + sso_unison_detune, + sso_unison_voices, + }; + SurgeSuperOscillator(SurgeStorage* storage, OscillatorStorage* oscdata, pdata* localcopy); virtual void init(float pitch, bool is_display = false) override; virtual void init_ctrltypes() override; diff --git a/src/common/dsp/WindowOscillator.cpp b/src/common/dsp/WindowOscillator.cpp index 60d2024ebfe..69605ce84af 100644 --- a/src/common/dsp/WindowOscillator.cpp +++ b/src/common/dsp/WindowOscillator.cpp @@ -58,7 +58,11 @@ void WindowOscillator::init(float pitch, bool is_display) float out_attenuation_inv = sqrt((float)NumUnison); OutAttenuation = 1.0f / (out_attenuation_inv * 16777216.f); - if (NumUnison == 1) + bool odd; + float mid; + int half; + + if (NumUnison == 1 || is_display) { DetuneBias = 1; DetuneOffset = 0; @@ -72,22 +76,25 @@ void WindowOscillator::init(float pitch, bool is_display) DetuneBias = (float)2.f / ((float)NumUnison - 1.f); DetuneOffset = -1.f; - bool odd = NumUnison & 1; - float mid = NumUnison * 0.5 - 0.5; - int half = NumUnison >> 1; + odd = NumUnison & 1; + mid = NumUnison * 0.5 - 0.5; + half = NumUnison >> 1; + } + if (!is_display) + { for (int i = 0; i < NumUnison; i++) { float d = fabs((float)i - mid) / mid; - + if (odd && (i >= half)) d = -d; if (i & 1) d = -d; - + Window.Gain[i][0] = limit_range((int)(float)(128.f * megapanL(d)), 0, 255); Window.Gain[i][1] = limit_range((int)(float)(128.f * megapanR(d)), 0, 255); - + if (oscdata->retrigger.val.b) Window.Pos[i] = (storage->WindowWT.size + ((storage->WindowWT.size * i) / NumUnison)) << 16; else From 8ea38464d4ab89c8283211badd81166b45136611 Mon Sep 17 00:00:00 2001 From: Mario Kruselj Date: Thu, 19 Nov 2020 15:59:27 +0100 Subject: [PATCH 2/3] Handle streaming for Window oscillator --- src/common/dsp/WindowOscillator.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/common/dsp/WindowOscillator.cpp b/src/common/dsp/WindowOscillator.cpp index 69605ce84af..c30b75cdf14 100644 --- a/src/common/dsp/WindowOscillator.cpp +++ b/src/common/dsp/WindowOscillator.cpp @@ -379,4 +379,11 @@ void WindowOscillator::handleStreamingMismatches(int streamingRevision, int curr oscdata->p[win_highcut].deactivated = true; oscdata->p[win_formant].set_type(ct_osc_feedback); } + if (streamingRevision < 14) + { + if (oscdata->p[win_unison_voices].val.i == 1) + { + oscdata->retrigger.val.b = true; + } + } } From bafb948341035960d27c7a5a625d45b9389056c1 Mon Sep 17 00:00:00 2001 From: Mario Kruselj Date: Thu, 19 Nov 2020 16:26:03 +0100 Subject: [PATCH 3/3] Update SurgeStorage.h Remove diode filter subtypes char array (unused) --- src/common/Parameter.cpp | 3 -- src/common/SurgeStorage.h | 82 +++++++++++++++++---------------------- 2 files changed, 36 insertions(+), 49 deletions(-) diff --git a/src/common/Parameter.cpp b/src/common/Parameter.cpp index 4888032e43a..f9451a421b7 100644 --- a/src/common/Parameter.cpp +++ b/src/common/Parameter.cpp @@ -2199,9 +2199,6 @@ void Parameter::get_display(char* txt, bool external, float ef) case fut_k35_hp: sprintf(txt, "%s", fut_k35_subtypes[i]); break; - case fut_diode: - sprintf(txt, "%s", fut_diode_subtypes[i]); - break; #if SURGE_EXTRA_FILTERS #endif default: diff --git a/src/common/SurgeStorage.h b/src/common/SurgeStorage.h index 1a5454c5f0c..f233f5ab82c 100644 --- a/src/common/SurgeStorage.h +++ b/src/common/SurgeStorage.h @@ -42,14 +42,21 @@ #define PATH_SEPARATOR '/' #endif -// patch layer - +const int n_scenes = 2; +const int n_scene_params = 271; const int n_oscs = 3; +const int n_osc_params = 7; +const int n_filterunits_per_scene = 2; const int n_lfos_voice = 6; const int n_lfos_scene = 6; const int n_lfos = n_lfos_voice + n_lfos_scene; -const int n_osc_params = 7; const int n_fx_params = 12; +const int n_fx_slots = 8; +const int n_global_params = 113; +const int n_global_postparams = 1; +const int n_total_params = n_global_params + 2 * n_scene_params + n_global_postparams; +const int metaparam_offset = 20480; // has to be bigger than total + (16 * 130) for fake VST3 mapping + const int FIRipol_M = 256; const int FIRipol_M_bits = 8; const int FIRipol_N = 12; @@ -57,26 +64,24 @@ const int FIRoffset = FIRipol_N >> 1; const int FIRipolI16_N = 8; const int FIRoffsetI16 = FIRipolI16_N >> 1; -const int n_fx_slots=8; - -// XML storage fileformat revision -// 0 -> 1 new EG attack shapes (0>1, 1>2, 2>2) -// 1 -> 2 new LFO EG stages (if (decay == max) sustain = max else sustain = min -// 2 -> 3 filter subtypes added comb should default to 1 and moog to 3 -// 3 -> 4 comb+/- combined into 1 filtertype (subtype 0,0->0 0,1->1 1,0->2 1,1->3 ) -// 4 -> 5 stereo filterconf now have seperate pan controls -// 5 -> 6 new filter sound in v1.2 (same parameters, but different sound & changed resonance response) -// 6 -> 7 custom controller state now stored (in seq. recall) -// 7 -> 8 larger resonance range (old filters are set to subtype 1), pan2 -> width -// 8 -> 9 now 8 macros (offset IDs larger than ctrl7 by +1), macros have names (guess for pre-rev9 patches) -// 9 -> 10 added character parameter -// 10 -> 11 (1.6.2 release) added DAW extra state -// 11 -> 12 (1.6.3 release) added new parameters to Distortion effect -// 12 -> 13 (1.7.0 release) parameter deactivation; sine LP/HP, sine/FM2/3 feedback extension/bipolar -// 13 -> 14 (1.8.0 release) add phaser stages parameter, add vocoder modulator input channel parameter, osc retrigger actually works for window/sine/FM2/FM3 - const int ff_revision = 14; +// XML storage file format revisions +// 0 -> 1: new EG attack shapes (0>1, 1>2, 2>2) +// 1 -> 2: new LFO EG stages (if (decay == max) sustain = max else sustain = min +// 2 -> 3: filter subtypes added - comb should default to 1 and ladder to 3 +// 3 -> 4: comb +/- combined into one filter type (subtype 0,0->0 0,1->1 1,0->2 1,1->3 ) +// 4 -> 5: stereo filter configuration now has seperate pan controls +// 5 -> 6: new filter sound in v1.2 (same parameters, but different sound and changed resonance response) +// 6 -> 7: macro state now stored (in DAW recall) +// 7 -> 8: larger resonance range (old filters are set to subtype 1), pan2 -> width +// 8 -> 9: now 8 macros (offset IDs larger than ctrl7 by +1), macros have names (guess for pre-rev9 patches) +// 9 -> 10: added Character parameter +// 10 -> 11: (1.6.2 release) added DAW extra state +// 11 -> 12: (1.6.3 release) added new parameters to Distortion effect +// 12 -> 13: (1.7.0 release) parameter deactivation; sine LP/HP, sine/FM2/3 feedback extension/bipolar +// 13 -> 14: (1.8.0 release) add phaser stages parameter, add vocoder modulator input channel parameter, osc retrigger actually works for window/sine/FM2/FM3 + extern float sinctable alignas(16)[(FIRipol_M + 1) * FIRipol_N * 2]; extern float sinctable1X alignas(16)[(FIRipol_M + 1) * FIRipol_N]; extern short sinctableI16 alignas(16)[(FIRipol_M + 1) * FIRipolI16_N]; @@ -89,14 +94,6 @@ extern float samplerate, samplerate_inv; extern double dsamplerate, dsamplerate_inv; extern double dsamplerate_os, dsamplerate_os_inv; -const int n_scene_params = 271; -const int n_global_params = 113; -const int n_global_postparams = 1; -const int n_total_params = n_global_params + 2 * n_scene_params + n_global_postparams; -const int metaparam_offset = 20480; // has to be bigger than total + 16 * 130 for fake VST3 mapping -const int n_scenes = 2; -const int n_filterunits_per_scene = 2; - enum scene_mode { sm_single = 0, @@ -506,11 +503,6 @@ const float fut_k35_saturations[5] = 4.0f }; -const char fut_diode_subtypes[1][32] = -{ - "24 dB/oct" -}; - const int fut_subcount[n_fu_type] = { 0, // fut_none @@ -602,8 +594,8 @@ struct MidiChannelState float timbre; }; -// I have used the ordering here in SurgeGUIEditor to iterate. Be careful if tyoe or retrigger move from first/last position. -struct OscillatorStorage : public CountedSetUserData // The counted set is the wt tables +// I have used the ordering here in SurgeGUIEditor to iterate. Be careful if type or retrigger move from first/last position. +struct OscillatorStorage : public CountedSetUserData // The counted set is the wavetables { Parameter type; Parameter pitch, octave; @@ -653,7 +645,7 @@ struct LFOStorage struct FxStorage { - // Just a heads up if you change this please go look at fx_reorder in SurgeSorage too + // Just a heads up: if you change this, please go look at fx_reorder in SurgeSorage too Parameter type; Parameter return_level; Parameter p[n_fx_params]; @@ -769,7 +761,7 @@ struct FormulaModulatorStorage { // Currently an unused placeholder }; /* -** There are a collection of things we want your DAW to save about your particular instance +** There is a collection of things we want your DAW to save about your particular instance ** but don't want saved in your patch. So have this extra structure in the patch which we ** can activate/populate from the DAW hosts. See #915 */ @@ -918,7 +910,8 @@ enum surge_copysource n_copysources, }; -/* STORAGE layer */ + + class alignas(16) SurgeStorage { @@ -926,7 +919,6 @@ class alignas(16) SurgeStorage float audio_in alignas(16)[2][BLOCK_SIZE_OS]; float audio_in_nonOS alignas(16)[2][BLOCK_SIZE]; float audio_otherscene alignas(16)[2][BLOCK_SIZE_OS]; // this will be a pointer to an aligned 2 x BLOCK_SIZE_OS array - // float sincoffset alignas(16)[(FIRipol_M)*FIRipol_N]; // deprecated SurgeStorage(std::string suppliedDataPath=""); @@ -974,7 +966,6 @@ class alignas(16) SurgeStorage void load_wt(int id, Wavetable* wt, OscillatorStorage *); void load_wt(std::string filename, Wavetable* wt, OscillatorStorage *); bool load_wt_wt(std::string filename, Wavetable* wt); - // void load_wt_wav(std::string filename, Wavetable* wt); void load_wt_wav_portable(std::string filename, Wavetable *wt); void export_wt_wav_portable(std::string fbase, Wavetable *wt); void clipboard_copy(int type, int scene, int entry); @@ -1011,7 +1002,6 @@ class alignas(16) SurgeStorage void loadMidiMappingByName( std::string name ); void storeMidiMappingToName( std::string name ); - // float table_sin[512],table_sin_offset[512]; std::mutex waveTableDataMutex; std::recursive_mutex modRoutingMutex; Wavetable WindowWT; @@ -1058,7 +1048,7 @@ class alignas(16) SurgeStorage std::unordered_map helpURL_controlgroup; std::unordered_map helpURL_paramidentifier; std::unordered_map helpURL_specials; - // Alterhately make this unordered and provide a hash + // Alternately make this unordered and provide a hash std::map, std::string> helpURL_paramidentifier_typespecialized; private: @@ -1104,8 +1094,8 @@ std::string appendDirectory( const std::string &root, const std::string &path1, } /* -** ToElement does a this && check to check nulls. (As does ToDocument and so on). -** gcc -O3 on linux optimizes that away giving crashes. So do this instead -** See github issue #469 +** ToElement does this && check to check nulls. (As does ToDocument and so on). +** gcc -O3 on Linux optimizes that away giving crashes. So do this instead +** See GitHub issue #469 */ #define TINYXML_SAFE_TO_ELEMENT(expr) ((expr)?(expr)->ToElement():NULL)