diff --git a/src/common/Parameter.cpp b/src/common/Parameter.cpp index 1ae2db12916..dd17d74bdb7 100644 --- a/src/common/Parameter.cpp +++ b/src/common/Parameter.cpp @@ -263,6 +263,8 @@ bool Parameter::can_extend_range() case ct_lfoamplitude: case ct_fmratio: case ct_reson_res_extendable: + case ct_freq_audible_with_tunability: + case ct_freq_audible_with_very_low_lowerbound: return true; } return false; @@ -419,6 +421,7 @@ void Parameter::set_type(int ctrltype) break; case ct_freq_audible: case ct_freq_audible_deactivatable: + case ct_freq_audible_with_tunability: valtype = vt_float; val_min.f = -60; val_max.f = 70; @@ -1020,6 +1023,7 @@ void Parameter::set_type(int ctrltype) case ct_freq_hpf: case ct_freq_audible: case ct_freq_audible_deactivatable: + case ct_freq_audible_with_tunability: case ct_freq_audible_with_very_low_lowerbound: case ct_freq_reson_band1: case ct_freq_reson_band2: @@ -2198,6 +2202,7 @@ void Parameter::get_display_alt(char *txt, bool external, float ef) case ct_freq_hpf: case ct_freq_audible: case ct_freq_audible_deactivatable: + case ct_freq_audible_with_tunability: case ct_freq_audible_with_very_low_lowerbound: case ct_freq_reson_band1: case ct_freq_reson_band2: @@ -3064,6 +3069,7 @@ bool Parameter::can_setvalue_from_string() case ct_envtime_linkable_delay: case ct_freq_audible: case ct_freq_audible_deactivatable: + case ct_freq_audible_with_tunability: case ct_freq_audible_with_very_low_lowerbound: case ct_freq_reson_band1: case ct_freq_reson_band2: diff --git a/src/common/Parameter.h b/src/common/Parameter.h index 73c07bd1036..4b532a7ba3a 100644 --- a/src/common/Parameter.h +++ b/src/common/Parameter.h @@ -64,6 +64,7 @@ enum ctrltypes ct_decibel_deactivatable, ct_freq_audible, ct_freq_audible_deactivatable, + ct_freq_audible_with_tunability, // we abuse 'extended' to mean 'use SCL tunign' ct_freq_audible_with_very_low_lowerbound, ct_freq_mod, ct_freq_hpf, diff --git a/src/common/SurgePatch.cpp b/src/common/SurgePatch.cpp index 8c103db761c..6d2ef9b2f5a 100644 --- a/src/common/SurgePatch.cpp +++ b/src/common/SurgePatch.cpp @@ -312,7 +312,7 @@ SurgePatch::SurgePatch(SurgeStorage *storage) Surge::Skin::Connector::connectorByID("filter.subtype_" + std::to_string(f + 1)), sc_id, cg_FILTER, f, false)); a->push_back(scene[sc].filterunit[f].cutoff.assign( - p_id.next(), id_s++, "cutoff", "Cutoff", ct_freq_audible, + p_id.next(), id_s++, "cutoff", "Cutoff", ct_freq_audible_with_tunability, Surge::Skin::Connector::connectorByID("filter.cutoff_" + std::to_string(f + 1)), sc_id, cg_FILTER, f, true, sceasy)); if (f == 1) diff --git a/src/common/SurgeStorage.h b/src/common/SurgeStorage.h index a6f17a014c5..46f4b3fc9ca 100644 --- a/src/common/SurgeStorage.h +++ b/src/common/SurgeStorage.h @@ -928,6 +928,7 @@ class alignas(16) SurgeStorage bool retuneToStandardTuning() { init_tables(); + currentTuning = twelveToneStandardMapping; return true; } diff --git a/src/common/SurgeSynthesizer.cpp b/src/common/SurgeSynthesizer.cpp index 4740c3ec4ff..ffc446617d4 100644 --- a/src/common/SurgeSynthesizer.cpp +++ b/src/common/SurgeSynthesizer.cpp @@ -2074,7 +2074,8 @@ bool SurgeSynthesizer::setParameter01(long index, float value, bool external, bo } else { - storage.getPatch().scene[s].filterunit[1].cutoff.set_type(ct_freq_audible); + storage.getPatch().scene[s].filterunit[1].cutoff.set_type( + ct_freq_audible_with_tunability); storage.getPatch().scene[s].filterunit[1].cutoff.set_name("Cutoff"); } need_refresh = true; diff --git a/src/common/dsp/FilterCoefficientMaker.cpp b/src/common/dsp/FilterCoefficientMaker.cpp index 7f984518cad..ed13474c94e 100644 --- a/src/common/dsp/FilterCoefficientMaker.cpp +++ b/src/common/dsp/FilterCoefficientMaker.cpp @@ -9,6 +9,8 @@ #include "filters/NonlinearFeedback.h" #include "filters/NonlinearStates.h" +#include "DebugHelpers.h" + using namespace std; const float smooth = 0.2f; @@ -16,9 +18,24 @@ const float smooth = 0.2f; FilterCoefficientMaker::FilterCoefficientMaker() { Reset(); } void FilterCoefficientMaker::MakeCoeffs(float Freq, float Reso, int Type, int SubType, - SurgeStorage *storageI) + SurgeStorage *storageI, bool tuningAdjusted) { storage = storageI; + if (tuningAdjusted && storage) + { + /* + * Alright frequency comes in in units of semitones from 440 / Midi Note 69. + */ + auto idx = (int)floor(Freq + 69); + float frac = (Freq + 69) - idx; // frac is 0 means use idx; frac is 1 means use idx+1 + + float b0 = storage->currentTuning.logScaledFrequencyForMidiNote(idx) * 12; + float b1 = storage->currentTuning.logScaledFrequencyForMidiNote(idx + 1) * 12; + + auto q = (1.f - frac) * b0 + frac * b1; + + Freq = q - 69; + } // Force compiler to error out if I miss one fu_type fType = (fu_type)Type; diff --git a/src/common/dsp/FilterCoefficientMaker.h b/src/common/dsp/FilterCoefficientMaker.h index 4db97dcd921..1d9b87bd390 100644 --- a/src/common/dsp/FilterCoefficientMaker.h +++ b/src/common/dsp/FilterCoefficientMaker.h @@ -6,7 +6,8 @@ const int n_cm_coeffs = 8; class FilterCoefficientMaker { public: - void MakeCoeffs(float Freq, float Reso, int Type, int SubType, SurgeStorage *storage); + void MakeCoeffs(float Freq, float Reso, int Type, int SubType, SurgeStorage *storage, + bool tuningAdjusted); void Reset(); FilterCoefficientMaker(); float C[n_cm_coeffs], dC[n_cm_coeffs], tC[n_cm_coeffs]; // K1,K2,Q1,Q2,V1,V2,V3,etc diff --git a/src/common/dsp/SurgeVoice.cpp b/src/common/dsp/SurgeVoice.cpp index 22f54c365fb..a95e8435ce1 100644 --- a/src/common/dsp/SurgeVoice.cpp +++ b/src/common/dsp/SurgeVoice.cpp @@ -1032,10 +1032,12 @@ void SurgeVoice::SetQFB(QuadFilterChainState *Q, int e) // Q == 0 means init(ial cutoffB += cutoffA; CM[0].MakeCoeffs(cutoffA, localcopy[id_resoa].f, scene->filterunit[0].type.val.i, - scene->filterunit[0].subtype.val.i, storage); + scene->filterunit[0].subtype.val.i, storage, + scene->filterunit[0].cutoff.extend_range); CM[1].MakeCoeffs( cutoffB, scene->f2_link_resonance.val.b ? localcopy[id_resoa].f : localcopy[id_resob].f, - scene->filterunit[1].type.val.i, scene->filterunit[1].subtype.val.i, storage); + scene->filterunit[1].type.val.i, scene->filterunit[1].subtype.val.i, storage, + scene->filterunit[1].cutoff.extend_range); for (int u = 0; u < n_filterunits_per_scene; u++) { diff --git a/src/common/dsp/effect/CombulatorEffect.cpp b/src/common/dsp/effect/CombulatorEffect.cpp index 2b2165a44cd..fdc1a14f2d4 100644 --- a/src/common/dsp/effect/CombulatorEffect.cpp +++ b/src/common/dsp/effect/CombulatorEffect.cpp @@ -165,12 +165,13 @@ void CombulatorEffect::process(float *dataL, float *dataR) /* * So now set up across the voices (e for 'entry' to match SurgeVoice) and the channels (c) */ + bool useTuning = fxdata->p[combulator_freq1].extend_range; for (int e = 0; e < 3; ++e) { for (int c = 0; c < 2; ++c) { coeff[e][c].MakeCoeffs(freq[e].v, fbscaled, type, - subtype | QFUSubtypeMasks::EXTENDED_COMB, storage); + subtype | QFUSubtypeMasks::EXTENDED_COMB, storage, useTuning); for (int i = 0; i < n_cm_coeffs; i++) { diff --git a/src/common/dsp/effect/ResonatorEffect.cpp b/src/common/dsp/effect/ResonatorEffect.cpp index 8e6e2e76624..47603d14bd9 100644 --- a/src/common/dsp/effect/ResonatorEffect.cpp +++ b/src/common/dsp/effect/ResonatorEffect.cpp @@ -168,7 +168,7 @@ void ResonatorEffect::process(float *dataL, float *dataR) for (int c = 0; c < 2; ++c) { coeff[e][c].MakeCoeffs(cutoff[e].v, resonance[e].v * rescomp[whichModel], type, subtype, - storage); + storage, false); for (int i = 0; i < n_cm_coeffs; i++) { diff --git a/src/common/gui/SurgeGUIEditor.cpp b/src/common/gui/SurgeGUIEditor.cpp index c779e3c3e8b..33655743831 100644 --- a/src/common/gui/SurgeGUIEditor.cpp +++ b/src/common/gui/SurgeGUIEditor.cpp @@ -3430,9 +3430,19 @@ int32_t SurgeGUIEditor::controlModifierClicked(CControl *control, CButtonState b { if (!(p->ctrltype == ct_fmratio && p->can_be_absolute() && p->absolute)) { - std::string txt = p->ctrltype == ct_reson_res_extendable - ? "Modulation Extends into Self-oscillation" - : "Extend Range"; + std::string txt = "Extend Range"; + switch (p->ctrltype) + { + case ct_reson_res_extendable: + txt = "Modulation Extends into Self-oscillation"; + break; + case ct_freq_audible_with_tunability: + case ct_freq_audible_with_very_low_lowerbound: + txt = "Filter Uses SCL/KBM Tuning"; + break; + default: + break; + } addCallbackMenu(contextMenu, Surge::UI::toOSCaseForMenu(txt), [this, p]() { p->extend_range = !p->extend_range; @@ -5044,7 +5054,7 @@ void SurgeGUIEditor::toggleTuning() mappingCacheForToggle = this->synth->storage.currentMapping.rawText; } this->synth->storage.remapToStandardKeyboard(); - this->synth->storage.init_tables(); + this->synth->storage.retuneToStandardTuning(); } if (statusTune)