diff --git a/src/common/SurgePatch.cpp b/src/common/SurgePatch.cpp index 5141cc21220..9be71fdc742 100644 --- a/src/common/SurgePatch.cpp +++ b/src/common/SurgePatch.cpp @@ -978,14 +978,18 @@ void SurgePatch::init_default_values() SurgePatch::~SurgePatch() { free(patchptr); } -void SurgePatch::copy_scenedata(pdata *d, int scene) +void SurgePatch::copy_scenedata(pdata *d, pdata *dUnmod, int scene) { + int s = scene_start[scene]; for (int i = 0; i < n_scene_params; i++) { // if (param_ptr[i+s]->valtype == vt_float) // d[i].f = param_ptr[i+s]->val.f; d[i].i = param_ptr[i + s]->val.i; + + if (param_ptr[i + s]->ctrlgroup == cg_OSC) + dUnmod[i].f = d[i].f; } for (int i = 0; i < paramModulationCount; ++i) @@ -1068,7 +1072,7 @@ void SurgePatch::update_controls( unsigned char mbuf alignas(16)[oscillator_buffer_size]; Oscillator *t_osc = - spawn_osc(sc.osc[osc].type.val.i, storage, &sc.osc[osc], nullptr, mbuf); + spawn_osc(sc.osc[osc].type.val.i, storage, &sc.osc[osc], nullptr, nullptr, mbuf); if (t_osc) { t_osc->init_ctrltypes(sn, osc); diff --git a/src/common/SurgeStorage.h b/src/common/SurgeStorage.h index f5b17b6b57c..ff23241ee73 100644 --- a/src/common/SurgeStorage.h +++ b/src/common/SurgeStorage.h @@ -1031,7 +1031,7 @@ class SurgePatch void init_default_values(); void update_controls(bool init = false, void *init_osc = 0, bool from_stream = false); void do_morph(); - void copy_scenedata(pdata *, int scene); + void copy_scenedata(pdata *, pdata *, int scene); void copy_globaldata(pdata *); // load/save @@ -1086,6 +1086,7 @@ class SurgePatch std::vector modulation_global; pdata scenedata[n_scenes][n_scene_params]; + pdata scenedataOrig[n_scenes][n_scene_params]; pdata globaldata[n_global_params]; void *patchptr; SurgeStorage *storage; diff --git a/src/common/SurgeSynthesizer.cpp b/src/common/SurgeSynthesizer.cpp index 6f2f0d3136e..a82f0ceb0db 100644 --- a/src/common/SurgeSynthesizer.cpp +++ b/src/common/SurgeSynthesizer.cpp @@ -87,6 +87,10 @@ SurgeSynthesizer::SurgeSynthesizer(PluginLayer *parent, const std::string &suppl // TODO: FIX SCENE ASSUMPTION memset(storage.getPatch().scenedata[0], 0, sizeof(pdata) * n_scene_params); memset(storage.getPatch().scenedata[1], 0, sizeof(pdata) * n_scene_params); + + memset(storage.getPatch().scenedataOrig[0], 0, sizeof(pdata) * n_scene_params); + memset(storage.getPatch().scenedataOrig[1], 0, sizeof(pdata) * n_scene_params); + memset(storage.getPatch().globaldata, 0, sizeof(pdata) * n_global_params); memset(mControlInterpolatorUsed, 0, sizeof(bool) * num_controlinterpolators); @@ -833,12 +837,12 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit int mpeMainChannel = getMpeMainChannel(channel, key); voices[scene].push_back(nvoice); - new (nvoice) SurgeVoice(&storage, &storage.getPatch().scene[scene], - storage.getPatch().scenedata[scene], key, velocity, channel, - scene, detune, &channelState[channel].keyState[key], - &channelState[mpeMainChannel], &channelState[channel], - mpeEnabled, voiceCounter++, host_noteid, - host_originating_key, host_originating_channel, 0.f, 0.f); + new (nvoice) SurgeVoice( + &storage, &storage.getPatch().scene[scene], storage.getPatch().scenedata[scene], + storage.getPatch().scenedataOrig[scene], key, velocity, channel, scene, detune, + &channelState[channel].keyState[key], &channelState[mpeMainChannel], + &channelState[channel], mpeEnabled, voiceCounter++, host_noteid, + host_originating_key, host_originating_channel, 0.f, 0.f); } } break; @@ -979,8 +983,9 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit storage.last_key[scene] = key; new (nvoice) SurgeVoice( &storage, &storage.getPatch().scene[scene], - storage.getPatch().scenedata[scene], key, velocity, channel, scene, detune, - &channelState[channel].keyState[key], &channelState[mpeMainChannel], + storage.getPatch().scenedata[scene], + storage.getPatch().scenedataOrig[scene], key, velocity, channel, scene, + detune, &channelState[channel].keyState[key], &channelState[mpeMainChannel], &channelState[channel], mpeEnabled, voiceCounter++, host_noteid, host_originating_key, host_originating_channel, aegReuse, fegReuse); @@ -1123,8 +1128,9 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit voices[scene].push_back(nvoice); new (nvoice) SurgeVoice( &storage, &storage.getPatch().scene[scene], - storage.getPatch().scenedata[scene], key, velocity, channel, scene, detune, - &channelState[channel].keyState[key], &channelState[mpeMainChannel], + storage.getPatch().scenedata[scene], + storage.getPatch().scenedataOrig[scene], key, velocity, channel, scene, + detune, &channelState[channel].keyState[key], &channelState[mpeMainChannel], &channelState[channel], mpeEnabled, voiceCounter++, host_noteid, host_originating_key, host_originating_channel, aegStart, fegStart); } @@ -4395,9 +4401,11 @@ void SurgeSynthesizer::processControl() // TODO: FIX SCENE ASSUMPTION if (playA) - storage.getPatch().copy_scenedata(storage.getPatch().scenedata[0], 0); // -""- + storage.getPatch().copy_scenedata(storage.getPatch().scenedata[0], + storage.getPatch().scenedataOrig[0], 0); // -""- if (playB) - storage.getPatch().copy_scenedata(storage.getPatch().scenedata[1], 1); + storage.getPatch().copy_scenedata(storage.getPatch().scenedata[1], + storage.getPatch().scenedataOrig[1], 1); // TODO: FIX SCENE ASSUMPTION. // Prior to 1.1 we could play before or after copying modulation data but as we diff --git a/src/common/dsp/Oscillator.cpp b/src/common/dsp/Oscillator.cpp index 9df63af45c3..fe7c2b58510 100644 --- a/src/common/dsp/Oscillator.cpp +++ b/src/common/dsp/Oscillator.cpp @@ -42,7 +42,7 @@ using namespace std; Oscillator *spawn_osc(int osctype, SurgeStorage *storage, OscillatorStorage *oscdata, - pdata *localcopy, unsigned char *onto) + pdata *localcopy, pdata *localcopyUnmod, unsigned char *onto) { static bool checkSizes = true; if (checkSizes) @@ -91,7 +91,7 @@ Oscillator *spawn_osc(int osctype, SurgeStorage *storage, OscillatorStorage *osc case ot_classic: return new (onto) ClassicOscillator(storage, oscdata, localcopy); case ot_wavetable: - return new (onto) WavetableOscillator(storage, oscdata, localcopy); + return new (onto) WavetableOscillator(storage, oscdata, localcopy, localcopyUnmod); case ot_window: { // In the event we are misconfigured, window oscillator will segfault. If you still play diff --git a/src/common/dsp/Oscillator.h b/src/common/dsp/Oscillator.h index 25b4869782a..bc21d5cc1f8 100644 --- a/src/common/dsp/Oscillator.h +++ b/src/common/dsp/Oscillator.h @@ -32,7 +32,7 @@ static constexpr size_t oscillator_buffer_size = 16 * 1024; Oscillator *spawn_osc(int osctype, SurgeStorage *storage, OscillatorStorage *oscdata, - pdata *localcopy, + pdata *localcopy, pdata *localcopyUnmod, unsigned char *onto); // This buffer should be at least oscillator_buffer_size #endif // SURGE_SRC_COMMON_DSP_OSCILLATOR_H diff --git a/src/common/dsp/SurgeVoice.cpp b/src/common/dsp/SurgeVoice.cpp index c40eece09ff..2f0cf8de53f 100644 --- a/src/common/dsp/SurgeVoice.cpp +++ b/src/common/dsp/SurgeVoice.cpp @@ -149,9 +149,9 @@ SurgeVoice::SurgeVoice() #endif } -SurgeVoice::SurgeVoice(SurgeStorage *storage, SurgeSceneStorage *oscene, pdata *params, int key, - int velocity, int channel, int scene_id, float detune, - MidiKeyState *keyState, MidiChannelState *mainChannelState, +SurgeVoice::SurgeVoice(SurgeStorage *storage, SurgeSceneStorage *oscene, pdata *params, + pdata *paramsUnmod, int key, int velocity, int channel, int scene_id, + float detune, MidiKeyState *keyState, MidiChannelState *mainChannelState, MidiChannelState *voiceChannelState, bool mpeEnabled, int64_t voiceOrder, int32_t host_nid, int16_t host_key, int16_t host_chan, float aegStart, float fegStart) @@ -165,6 +165,7 @@ SurgeVoice::SurgeVoice(SurgeStorage *storage, SurgeSceneStorage *oscene, pdata * this->storage = storage; this->scene = oscene; this->paramptr = params; + this->paramptrUnmod = paramsUnmod; this->mpeEnabled = mpeEnabled; this->host_note_id = host_nid; this->originating_host_key = host_key; @@ -527,7 +528,7 @@ void SurgeVoice::switch_toggled() { bool nzid = scene->drift.extend_range; osc[i] = spawn_osc(scene->osc[i].type.val.i, storage, &scene->osc[i], localcopy, - oscbuffer[i]); + this->paramptrUnmod, oscbuffer[i]); if (osc[i]) { // this matches the override in ::process_block diff --git a/src/common/dsp/SurgeVoice.h b/src/common/dsp/SurgeVoice.h index f5122b7a553..e18e7859e68 100644 --- a/src/common/dsp/SurgeVoice.h +++ b/src/common/dsp/SurgeVoice.h @@ -39,17 +39,18 @@ class alignas(16) SurgeVoice float output alignas(16)[2][BLOCK_SIZE_OS]; lipol_ps osclevels alignas(16)[7]; pdata localcopy alignas(16)[n_scene_params]; + pdata localcopy2 alignas(16)[n_scene_params]; float fmbuffer alignas(16)[BLOCK_SIZE_OS]; // used for the 2>1<3 FM-mode (Needs the pointer earlier) SurgeVoice(); - SurgeVoice(SurgeStorage *storage, SurgeSceneStorage *scene, pdata *params, int key, - int velocity, int channel, int scene_id, float detune, MidiKeyState *keyState, - MidiChannelState *mainChannelState, MidiChannelState *voiceChannelState, - bool mpeEnabled, int64_t voiceOrder, int32_t host_note_id, - int16_t originating_host_key, int16_t originating_host_channel, float aegStart, - float fegStart); + SurgeVoice(SurgeStorage *storage, SurgeSceneStorage *scene, pdata *params, pdata *paramsUnmod, + int key, int velocity, int channel, int scene_id, float detune, + MidiKeyState *keyState, MidiChannelState *mainChannelState, + MidiChannelState *voiceChannelState, bool mpeEnabled, int64_t voiceOrder, + int32_t host_note_id, int16_t originating_host_key, int16_t originating_host_channel, + float aegStart, float fegStart); ~SurgeVoice(); void release(); @@ -261,8 +262,9 @@ class alignas(16) SurgeVoice // data int lag_id[8], pitch_id, octave_id, volume_id, pan_id, width_id; - SurgeSceneStorage *scene, *origscene; + SurgeSceneStorage *scene; pdata *paramptr; + pdata *paramptrUnmod; int route[6]; float octaveSize = 12.0f; diff --git a/src/common/dsp/oscillators/WavetableOscillator.cpp b/src/common/dsp/oscillators/WavetableOscillator.cpp index 35ad31ab582..d9c8738030d 100644 --- a/src/common/dsp/oscillators/WavetableOscillator.cpp +++ b/src/common/dsp/oscillators/WavetableOscillator.cpp @@ -32,9 +32,10 @@ using namespace std; const float hpf_cycle_loss = 0.99f; WavetableOscillator::WavetableOscillator(SurgeStorage *storage, OscillatorStorage *oscdata, - pdata *localcopy) + pdata *localcopy, pdata *localcopyUnmod) : AbstractBlitOscillator(storage, oscdata, localcopy) { + unmodulatedLocalcopy = localcopyUnmod; } WavetableOscillator::~WavetableOscillator() {} @@ -85,8 +86,8 @@ void WavetableOscillator::init(float pitch, bool is_display, bool nonzero_init_d // rather than wavetable from first to second to last frame nointerp = !oscdata->p[wt_morph].extend_range; - float shape = l_shape.v; - + float shape = limit_range( + l_shape.v + (localcopy[id_shape].f - unmodulatedLocalcopy[id_shape].f), 0.f, 1.f); float intpart; shape *= ((float)oscdata->wt.n_tables - 1.f + nointerp) * 0.99999f; tableipol = shape; @@ -377,7 +378,7 @@ template void WavetableOscillator::update_lagvals() l_hskew.newValue(limit_range(localcopy[id_hskew].f, -1.f, 1.f)); float a = limit_range(localcopy[id_clip].f, 0.f, 1.f); l_clip.newValue(-8 * a * a * a); - l_shape.newValue(limit_range(localcopy[id_shape].f, 0.f, 1.f)); + l_shape.newValue(unmodulatedLocalcopy[id_shape].f); formant_t = max(0.f, localcopy[id_formant].f); float invt = min(1.0, (8.175798915 * storage->note_to_pitch_tuningctr(pitch_t)) * @@ -440,7 +441,8 @@ void WavetableOscillator::process_block(float pitch0, float drift, bool stereo, last_tableipol = tableipol; last_tableid = tableid; - float shape = l_shape.v; + float shape = limit_range( + l_shape.v + (localcopy[id_shape].f - unmodulatedLocalcopy[id_shape].f), 0.f, 1.f); float intpart; shape *= ((float)oscdata->wt.n_tables - 1.f + nointerp) * 0.99999f; tableipol = shape; diff --git a/src/common/dsp/oscillators/WavetableOscillator.h b/src/common/dsp/oscillators/WavetableOscillator.h index 28b98486ee9..37bf4d23a50 100644 --- a/src/common/dsp/oscillators/WavetableOscillator.h +++ b/src/common/dsp/oscillators/WavetableOscillator.h @@ -43,7 +43,8 @@ class WavetableOscillator : public AbstractBlitOscillator }; lipol_ps li_hpf, li_DC, li_integratormult; - WavetableOscillator(SurgeStorage *storage, OscillatorStorage *oscdata, pdata *localcopy); + WavetableOscillator(SurgeStorage *storage, OscillatorStorage *oscdata, pdata *localcopy, + pdata *localcopyUnmod); virtual void init(float pitch, bool is_display = false, bool nonzero_init_drift = true) override; virtual void init_ctrltypes() override; @@ -72,6 +73,7 @@ class WavetableOscillator : public AbstractBlitOscillator int nointerp; float FMmul_inv; int sampleloop; + pdata *unmodulatedLocalcopy; }; #endif // SURGE_SRC_COMMON_DSP_OSCILLATORS_WAVETABLEOSCILLATOR_H diff --git a/src/surge-testrunner/UnitTestsDSP.cpp b/src/surge-testrunner/UnitTestsDSP.cpp index 42b628bbc56..4cdfa4d9388 100644 --- a/src/surge-testrunner/UnitTestsDSP.cpp +++ b/src/surge-testrunner/UnitTestsDSP.cpp @@ -715,8 +715,8 @@ TEST_CASE("Oscillator Onset", "[dsp]") // See issue 7570 oscstorage->retrigger.val.b = rt; - auto o = - spawn_osc(ot, storage, oscstorage, storage->getPatch().scenedata[0], oscbuffer); + auto o = spawn_osc(ot, storage, oscstorage, storage->getPatch().scenedata[0], + storage->getPatch().scenedataOrig[0], oscbuffer); o->init_ctrltypes(); o->init_default_values(); o->init_extra_config(); diff --git a/src/surge-testrunner/UnitTestsMOD.cpp b/src/surge-testrunner/UnitTestsMOD.cpp index eac42066bf9..a7dfe5d5372 100644 --- a/src/surge-testrunner/UnitTestsMOD.cpp +++ b/src/surge-testrunner/UnitTestsMOD.cpp @@ -817,7 +817,8 @@ TEST_CASE("LFO Tempo Sync Drift in Latch Mode", "[mod]") surge->setParameter01(rid, 0.455068, false, false); lfostorage->shape.val.i = lt_square; - surge->storage.getPatch().copy_scenedata(surge->storage.getPatch().scenedata[0], 0); + surge->storage.getPatch().copy_scenedata(surge->storage.getPatch().scenedata[0], + surge->storage.getPatch().scenedataOrig[1], 0); lfo->assign(&(surge->storage), lfostorage, surge->storage.getPatch().scenedata[0], nullptr, ss.get(), nullptr, nullptr); diff --git a/src/surge-xt/gui/widgets/OscillatorWaveformDisplay.cpp b/src/surge-xt/gui/widgets/OscillatorWaveformDisplay.cpp index 638b586d01c..bc80a773464 100644 --- a/src/surge-xt/gui/widgets/OscillatorWaveformDisplay.cpp +++ b/src/surge-xt/gui/widgets/OscillatorWaveformDisplay.cpp @@ -525,7 +525,7 @@ ::Oscillator *OscillatorWaveformDisplay::setupOscillator() tp[oscdata->p[i].param_id_in_scene].i = oscdata->p[i].val.i; } - return spawn_osc(oscdata->type.val.i, storage, oscdata, tp, oscbuffer); + return spawn_osc(oscdata->type.val.i, storage, oscdata, tp, tp, oscbuffer); } void OscillatorWaveformDisplay::populateMenu(juce::PopupMenu &contextMenu, int selectedItem,