Skip to content

Commit

Permalink
Rename a few APIs (#6056)
Browse files Browse the repository at this point in the history
Closes #5569
  • Loading branch information
mkruselj authored Apr 12, 2022
1 parent efa2f4e commit f0a5261
Show file tree
Hide file tree
Showing 16 changed files with 136 additions and 134 deletions.
117 changes: 58 additions & 59 deletions doc/FX Lifecycle.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,42 @@
# FX Lifecycle
# Effects Lifecycle

This documents how FX are created, swapped, initialized, etc... as of
8e03ba0b54b09 or so.
This documents how effects are created, swapped, initialized, etc... as of commit `8e03ba0b54b09` or so.

There's lots of cases. Lets enumerate all of them
There's lots of cases. Let's enumerate all of them:

## Core data structures and Functions

* `spawn_effect` creates an instance of an Effect with the appropriate type bound to a storage and data
* `spawn_effect` creates an instance of an `Effect` with the appropriate type bound to a storage and data
* `loadFx` compares the synth state with the runtime state and optionally reloads effects
* the first argument `initp` if true means the new effect calls `init_default_values` and if false
just forces all params within their default ranges
* the second `force_reload_all` makes all fx reload
* loadFX begins in all cases where a type switches setting all params to type 'none'.
* the second `force_reload_all` makes all effects reload
* `loadFx` begins in all cases where a type switches setting all params to type 'none'.
* `loadFx` is called from three places
* synth::processThreadunsafeOperations if `load_fx_needed` is true with false/false
* synth::processControl if `load_fx_needed` with false/false
* synth::loadRaw in all cases with false/true (namely, force a full reload on patch load)
* If a reload of either form has changed, the `updateAfterReload()` virtual method is called on the FX
* `load_fx_needed` is a bool on SurgeSynth
* triggers a call to loadFx in processControlEV
* `synth::processAudioThreadOpsWhenAudioEngineUnavailable` if `load_fx_needed` is true with false/false
* `synth::processControl` if `load_fx_needed` with false/false
* `synth::loadRaw` in all cases with false/true (namely, force a full reload on patch load)
* If a reload of either form has changed, the `updateAfterReload()` virtual method is called on the effect
* `load_fx_needed` is a bool on `SurgeSynth`
* triggers a call to `loadFx` in `processControlEV`
* set to true in the constructor of synth
* set to true on a setParameter01 of ct_fxtype (so automation path)
* set to false in loadFx
* set to true in SGE when you have a tag_fx_menu
* The patch has the `fx[n_fx]` array which holds the FXStorage pointers
* set to true on a `setParameter01` of `ct_fxtype` (so automation path)
* set to false in `loadFx`
* set to true in `SurgeGUIEeditor` when you have a `tag_fx_menu`
* The patch has the `fx[n_fx]` array which holds the `FXStorage` pointers
* The synth has an `FXStorage *fxsync` which is malloced to `n_fx_slots * sizeof(FXStorage)`. This
object acts as an edit buffer for external editors who want to bulk update the type of objects.
* In the synth constructor it is set to the value of `patch.fx[i]` for each i
* In SurgeGUIEditor the fxsync is used as the storage which is edited by the CSnapshotMenu, not
object acts as an edit buffer for external editors which want to bulk update the type of objects.
* In the synth constructor it is set to the value of `patch.fx[i]` for each `i`
* In `SurgeGUIEditor` the `fxsync` is used as the storage which is edited by the `XMLConfiguredMenus`, not
the patch buffer
* In `loadFX`
* if reloading an FX, copy from the sync to the patch. This defacto means "take the sync values as
they have been set by a UI"
* Similarly if fx_reload[s] is true, copy from the sync and syspend and init the FX
* In `loadFx`
* if reloading an effect, copy from the sync to the patch. This de facto means "take the sync values as
they have been set by the GUI"
* Similarly if `fx_reload[s]` is true, copy from the sync, thensuspend and initialize the effect
* The slot `fx_reload[n_fx_slots]`
* This is set by SurgeGUIEditor primarily to force a reload of an FX in the event that the type has not changed
* This is set by `SurgeGUIEditor` primarily to force a reload of an effect in the event that the type has not changed

## Empty FX to Loaded FX, FX On
## Empty effect to loaded effect, effect enabled

Spawn Effect is called twice

Expand All @@ -60,62 +59,62 @@ Spawn Effect id=6
[ 6]: 6 Surge 0x0000000150640620 _ZN6AUBase9RenderBusERjRK14AudioTimeStampjj + 96
```

* CSnapshotMenu::loadSnapshot
* Load an FX and call `init_ctrltypes` and `init_default_values`. This sets up the params on the control to defaults
in the storage bound to the FX
* delete the FX
* Parse the XML and set the fxbuffer parameter values as described in the FX. This leaves the storage configured with
values and types but does not leave the synth with the FX setup
* raise a valueChanged so SurgeGuiEditor sets load_fx_needed to true.
* If audio_processing_active is false it also sets up the FX from the UI thread via the
call to synth->processThreadunsafeOperations() but we will ignroe that case in this document
* `XMLConfiguredMenus::loadSnapshot`
* Load an effect and call `init_ctrltypes` and `init_default_values`. This sets up the params on the control to defaults
in the storage bound to the effect
* delete the effect
* Parse the XML and set the `fxbuffer` parameter values as described in the effect. This leaves the storage configured with
values and types but does not leave the synth with the effect setup
* raise a `valueChanged` so `SurgeGUIEditor` sets `load_fx_needed` to true.
* If `audio_processing_active` is false it also sets up the effect from the UI thread via the
call to `synth->processAudioThreadOpsWhenAudioEngineUnavailable()`, but we will ignroe that case in this document
from hereon out
* SurgeSynthesizer
* In the next `process()` and call to `processControl` `load_fx_needed` will be true
* so call `loadFx(false,false)` which will force FX where the type in the FX to be noticed as
changed and will spawn an fx
* `SurgeSynthesizer`
* In the next `process()` and call to `processControl`, `load_fx_needed` will be true
* so call `loadFx(false, false)` which will force effect where the type in the effect is to be noticed as
changed and will spawn an effect
* but since `initp` is false, don't call `init_default_values` and instead continue to use the values
left lingering in the FX from the invocation in CSnapshotMenu.
left lingering in the effect from the invocation in `XMLConfiguredMenus`.

## Loaded FX to Off, FX On
## Loaded effect to disabled, effect enabled

* CSnapshotMenu calls loadSnapshot with type 0 and null XML (fine).
* This sets the valtype to 0 and then SGE gets the message
* `XMLConfiguredMenus` calls `loadSnapshot` with type 0 and null XML (fine).
* This sets `valtype` to 0 and then `SurgeGUIEditor` gets the message
* Question: What sets the types to none everywhere?

## Empty FX to Loaded FX, FX Bypassed
## Empty effect to loaded effect, effect bypassed

## Active FX to New preset of Same Type, FX On
## Active effect to new preset of same type, effect on

* CSnapshotMenu
* `XMLConfiguredMenus`

## Active FX to New FX of Different Type, FX On
## Active effect to new effect of different type, effect on

## Active FX to New FX, FX Bypassed
## Active effect to new effect, effect bypassed

Question: is bypass from CEG different than global bypass?

## Load a patch with FX Off
## Load a patch with effect off

## Load a patch with active FX
## Load a patch with active effect

## Load a User FX setting
## Load a User effect setting

* CFXMenu::loadUserPreset looks like loadSnapshot in that it creates a new FX on the sync buffer, applies the value
from the preset, and raises the event
* `FXMenu::loadUserPreset` looks like `loadSnapshot` in that it creates a new effect on the sync buffer, applies the value
from the preset, and raises an event
* After that it's just like a same-type load

## Swap an FX
## Swap an effect

## An Airwindows FX is activated and changes type
## An Airwindows effect is activated and changes type

## An Airwindows FX is de-activated and changes type
## An Airwindows effect is deactivated and changes type

## A non-Airwindows FX becomes an Airwindows FX in activated state
## A non-Airwindows effect becomes an Airwindows effect in activated state

## A non-Airwindow FX becomes an Airwindows FX in deactivated state
## A non-Airwindow effect becomes an Airwindows effect in deactivated state

## The DAW Automation Path
## The DAW automation path

The DAW automation path comes in through `synth::setParameter01` and handles the `ct_fxtype` to do the spawn-and-replace.
See the comments there for more.
6 changes: 3 additions & 3 deletions scripts/ipy/Demonstrate Surge in Python.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@
"patches make extensive use of the modulation features. This python API allows you to\n",
"query and set up modulation using the same API that the UI uses when in modulation armed mode.\n",
"\n",
"The three key APIs we have are getModSource, setModulation and getModulation. So lets\n",
"The three key APIs we have are getModSource, setModDepth01 and getModDepth01. So lets\n",
"make an ADSR with an LFO wiggle, and the constants have ms_ constants defined"
]
},
Expand Down Expand Up @@ -521,12 +521,12 @@
"osc0lev = cg.getEntries()[0].getParams()[0]\n",
"lfo1 = surge.getModSource(srgco.ms_lfo1)\n",
"\n",
"surge.setModulation( osc0lev, lfo1, 0.1 )\n",
"surge.setModDepth01( osc0lev, lfo1, 0.1 )\n",
"\n",
"\n",
"window = fourSecondsRMS( surge )\n",
"plt.plot( window )\n",
"print( \"Modulation to osc0lev from lfo1 is: \", surge.getModulation( osc0lev, lfo1 ))"
"print( \"Modulation to osc0lev from lfo1 is: \", surge.getModDepth01( osc0lev, lfo1 ))"
]
},
{
Expand Down
20 changes: 11 additions & 9 deletions src/common/SurgeSynthesizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ SurgeSynthesizer::SurgeSynthesizer(PluginLayer *parent, const std::string &suppl
pid++;
}
if (patchid_queue >= 0)
processThreadunsafeOperations(true); // DANGER MODE IS ON
processAudioThreadOpsWhenAudioEngineUnavailable(true); // DANGER MODE IS ON
patchid_queue = -1;
}

Expand Down Expand Up @@ -2350,8 +2350,10 @@ bool SurgeSynthesizer::setParameter01(long index, float value, bool external, bo
int cge = p->ctrlgroup_entry;

fxsync[cge].type.val.i = p->val.i;
p->val.i = oldval.i; // so funnily we want to set the value *back* so the loadFX
// picks up the change in fxsync

// so funnily we want to set the value *back* so that loadFx picks up the change in
// fxsync
p->val.i = oldval.i;
Effect *t_fx = spawn_effect(fxsync[cge].type.val.i, &storage, &fxsync[cge], 0);
if (t_fx)
{
Expand Down Expand Up @@ -2577,7 +2579,7 @@ bool SurgeSynthesizer::loadFx(bool initp, bool force_reload_all)
{
for (auto &t : fxmodsync[s])
{
setModulation(storage.getPatch().fx[s].p[t.whichForReal].id,
setModDepth01(storage.getPatch().fx[s].p[t.whichForReal].id,
(modsources)t.source_id, t.source_scene, t.source_index,
t.depth);
}
Expand Down Expand Up @@ -3051,7 +3053,7 @@ bool SurgeSynthesizer::isModsourceUsed(modsources modsource)
return modsourceused[modsource];
}

float SurgeSynthesizer::getModulation(long ptag, modsources modsource, int modsourceScene,
float SurgeSynthesizer::getModDepth01(long ptag, modsources modsource, int modsourceScene,
int index) const
{
if (!isValidModulation(ptag, modsource))
Expand Down Expand Up @@ -3216,7 +3218,7 @@ void SurgeSynthesizer::clearModulation(long ptag, modsources modsource, int mods
}
}

bool SurgeSynthesizer::setModulation(long ptag, modsources modsource, int modsourceScene, int index,
bool SurgeSynthesizer::setModDepth01(long ptag, modsources modsource, int modsourceScene, int index,
float val)
{
if (!isValidModulation(ptag, modsource))
Expand Down Expand Up @@ -3483,7 +3485,7 @@ void loadPatchInBackgroundThread(SurgeSynthesizer *sy)
return;
}

void SurgeSynthesizer::processThreadunsafeOperations(bool dangerMode)
void SurgeSynthesizer::processAudioThreadOpsWhenAudioEngineUnavailable(bool dangerMode)
{
if (!audio_processing_active || dangerMode)
{
Expand Down Expand Up @@ -4488,7 +4490,7 @@ void SurgeSynthesizer::reorderFx(int source, int target, FXReorderMode m)
}

auto depth =
getModulation(fxsync[source].p[whichForReal].id, (modsources)mv->at(i).source_id,
getModDepth01(fxsync[source].p[whichForReal].id, (modsources)mv->at(i).source_id,
mv->at(i).source_scene, mv->at(i).source_index);
fxmodsync[target].push_back({mv->at(i).source_id, mv->at(i).source_scene,
mv->at(i).source_index, whichForReal, depth});
Expand All @@ -4510,7 +4512,7 @@ void SurgeSynthesizer::reorderFx(int source, int target, FXReorderMode m)
}
}

auto depth = getModulation(fxsync[target].p[whichForReal].id,
auto depth = getModDepth01(fxsync[target].p[whichForReal].id,
(modsources)mv->at(i).source_id, mv->at(i).source_scene,
mv->at(i).source_index);
fxmodsync[source].push_back({mv->at(i).source_id, mv->at(i).source_scene,
Expand Down
17 changes: 9 additions & 8 deletions src/common/SurgeSynthesizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,14 @@ class alignas(16) SurgeSynthesizer
void resetStateFromTimeData();
void processControl();
/*
* processThreadunsafeOperations reloads a patch if the audio thread isn't running
* but if it is running lets the deferred queue handle it. But it has an option
* processAudioThreadOpsWhenAudioEngineUnavailable reloads a patch if the audio thread
* isn't running but if it is running lets the deferred queue handle it. But it has an option
* which is *extremely dangerous* to force you to load in the current thread immediately.
* If you use this option and dont' know what you are doing it will explode - basically
* we only use it in the startup constructor path.
*/
void processThreadunsafeOperations(bool doItEvenIfAudioIsRunningDANGER = false);
void
processAudioThreadOpsWhenAudioEngineUnavailable(bool doItEvenIfAudioIsRunningDANGER = false);
bool loadFx(bool initp, bool force_reload_all);
void enqueueFXOff(int whichFX);
bool loadOscalgos();
Expand Down Expand Up @@ -279,22 +280,22 @@ class alignas(16) SurgeSynthesizer
int index) const;

/*
* setModulation etc take a modsource scene. This is only needed for global modulations
* setModDepth01 etc take a modsource scene. This is only needed for global modulations
* since for in-scene modulations the parameter implicit in ptag has a scene. But for
* LFOs modulating FX, we need to know which scene they originate from. See #2285
*/
bool setModulation(long ptag, modsources modsource, int modsourceScene, int index, float value);
float getModulation(long ptag, modsources modsource, int modsourceScene, int index) const;
bool setModDepth01(long ptag, modsources modsource, int modsourceScene, int index, float value);
float getModDepth01(long ptag, modsources modsource, int modsourceScene, int index) const;
float getModDepth(long ptag, modsources modsource, int modsourceScene, int index) const;
void muteModulation(long ptag, modsources modsource, int modsourceScene, int index, bool mute);
bool isModulationMuted(long ptag, modsources modsource, int modsourceScene, int index) const;
float getModDepth(long ptag, modsources modsource, int modsourceScene, int index) const;
void clearModulation(long ptag, modsources modsource, int modsourceScene, int index,
bool clearEvenIfInvalid = false);
void clear_osc_modulation(
int scene, int entry); // clear the modulation routings on the algorithm-specific sliders

/*
* The modulation API (setModulation etc...) is called from all sorts of places
* The modulation API (setModDepth01 etc...) is called from all sorts of places
* but mostly from the UI. This adds a listener which gets notified but this listener
* should just post a message over to the UI thread and be quick in case there
* are cases where the audio thread calls set/clear/mute modulation.
Expand Down
4 changes: 2 additions & 2 deletions src/common/SurgeSynthesizerIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ void SurgeSynthesizer::jogPatch(bool increment, bool insideCategory)

patchid_queue = storage.patchOrdering[order];
}
processThreadunsafeOperations();
processAudioThreadOpsWhenAudioEngineUnavailable();
return;
}

Expand Down Expand Up @@ -169,7 +169,7 @@ void SurgeSynthesizer::jogCategory(bool increment)
if (storage.patch_list[p].category == current_category_id)
{
patchid_queue = p;
processThreadunsafeOperations();
processAudioThreadOpsWhenAudioEngineUnavailable();
return;
}
}
Expand Down
Loading

0 comments on commit f0a5261

Please sign in to comment.