4 steps to make an effect stateless
Let use use EffectEcho as an example.
"Define Settings" (reference commit: 2d02c26)
In header file:
In cpp file:
"Define validator" (reference commit: 2592061)
In header file:
you can remove forward declarations like:
class wxCheckBox; class wxSlider; class wxSpinCtrl;
add line "
struct Validator;
" in public sectionif the following is declared, remove it:
bool TransferDataToWindow(const EffectSettings &settings) override;
In cpp file:
reimplement
Effect::PopulateOrExchange
:
ADDITIONAL STEPS for an effect which has explicitly declared Sliders, Textboxes etc.
remove
DECLARE_EVENT_TABLE();
(in .h)remove the definition of the
enum
withID_***
(in .cpp)remove the section
BEGIN_EVENT_TABLE... END_EVENT_TABLE
(in .cpp)cut (from .h) all declarations of wxWidgets (
wxTextCtrl
,wxSlider
etc.) and paste them in the validator definition (in .cpp)cut (from .h) all declarations of handlers like
void On
Slider
,On
Text
and paste them in the validator definition (in .cpp) IMPORTANT: at the end of all the handlers, add a call toValidateUI()
;in
Validator::PopulateOrExchange
remove the calls to
.Id(...)
on the ShuttleGuijust after every creation of a widget, add a line like:
where: 1st arg is the just created widget 2nd arg is either
wxEVT_TEXT
orwxEVT_SLIDER
orwxEVT_CHECKBOX
according to the widget type 3rd arg is the handler which is meant to handle events coming from the widgetchange
bool EffectWahwah::TransferDataToWindow(const EffectSettings &)
tobool EffectWahwah::Validator::UpdateUI()
leaving the contents which is there, but adding at the top:
in each of the control handlers:
add
Validator::
just afterMyEffect::
in the method definitionplace a call to
ValidateUI();
at the end
there might be calls to
Effect::EnableApply
. Solve them by casting down to class Effect the Validator'smEffect
member.
"Define the instance"
(reference commit: 5961be1)
however, since that commit, the following has changed:
some methods of Instance have a different signature
MakeInstance
now takes no argsthe instance does not inherit
EffectInstanceWithSampleRate
anymore (?))
In header file:
add in public section:
Identify members that represent state - comment them out
remove decls of methods:
ProcessInitialize, ProcessFinalize, ProcessBlock RealtimeInitialize, RealtimeAddProcessor, RealtimeFinalize, RealtimeProcess
In cpp file:
add:
Make the
Effect::Process***
methods belong to the instance by:prepending
::Instance
where neededreplacing the lines you added in step 1:
auto& echoSettings = mSettings;
with this
// temporary - in the final step this will be replaced by // auto& echoSettings = GetSettings(settings); // auto& echoSettings = static_cast<const EffectEcho&>(mProcessor).mSettings;
"Real Statelessness" (reference commit: 231bf76)
In header file:
make the effect inherit:
EffectWithSettings<EffectEchoSettings, PerTrackEffect>
if the effect is per-trackEffectWithSettings<EffectEchoSettings, Effect>
otherwise
remove the FetchParameters method
remove mSettings member
In cpp file:
add const to arg
EffectEchoSettings&
in the Validator constructorturn
Validator::mSettings
from a reference to a non-referencego to
ProcessInitialize, ProcessBlock, ProcessFinalize, RealtimeInitialize
etc.; remove the downcast and enable the commented outGetSettings
go to
Validator::ValidateUI
, uncomment the line//EffectEcho::GetSettings(settings) = mSettings;
go to
Validator::UpdateUI
, uncomment the line//mSettings = GetSettings(settings);
in the effect's
::PopulateOrExchange
, uncomment/comment lines as prescribedin general, settings are not taken anymore from the effect, like in:
auto& ms = static_cast<const EffectWahwah&>(mProcessor).mSettings;
but taking them from the passed ones, like so:
auto& ms = GetSettings(settings);
Some notes by Paul Licameli:
Regarding step 2:
"Another possibility, is that I MAY put all the needed calls to GetSettings()
in early, BUT I do not yet use EffectWithSettings<>
and the definition of GetSettings()
that it generates. Instead, I define GetSettings()
to ignore its argument and return mSettings
in the effect object. At the last step of real statelessness, I can delete the definition of GetSettings()
but leave the calls."
Regarding step 4:
"There may be a fifth step: see the commit I recently pushed to the branch for ladspa effects.
MakeSettings
and CopySettingsContents
may require explicit definitions instead of those generated by the template EffectWithSettings
.
That does not apply to built-in effects where the settings structure simply contains several scalar fields and no variable sized containers.
For Ladspa, you can see that MakeSettings()
instead allocates a vector of values, whose size is not known at compilation time but depends on other information that the effect is queried for. So default construction of the settings, as in the generated MakeSettings()
, will not do.
Then CopySettingsContents
must also be overridden, not for correctness but for efficiency. The override is supposed to avoid memory allocations when the main thread calls it, so it should not use the copy constructors of the embedded std::vector
but instead rewrite an existing vector without changing its capacity. The assumption will be made that the destination settings containers are already correctly sized, because MakeSettings did that, or else full copies done in the main thread (where allocations are allowed) already did that.
Other third party effect families like AudioUnits may have Settings structures containing maps from strings to values, instead of vectors, but the problems will be similar."
Last updated