20#include <condition_variable>
42 pOutputs ? pOutputs->
Clone() :
nullptr } });
44 pOutputs ? pOutputs->
Clone() :
nullptr } });
56 std::unique_ptr<EffectInstance::Message> pMessage) {
62 std::unique_ptr<EffectInstance::Message> pMessage) {
64 counter, std::move(pMessage) });
111 if (pOutputs && slot.mResponse.pOutputs)
112 pOutputs->
Assign(std::move(*slot.mResponse.pOutputs));
113 counter = slot.mResponse.counter;
139 mMessage.SettingsAndCounter::swap(message);
159 if(slot.mMessage.counter ==
settings.counter)
161 settings.counter = slot.mMessage.counter;
165 slot.mMessage.settings,
settings.settings);
166 settings.settings.extra = slot.mMessage.settings.extra;
169 state.
mMovedMessage->Assign(std::move(*slot.mMessage.pMessage));
185 std::condition_variable
mCV;
194 :
mwState{ state.weak_from_this() }
201 if (
auto pState =
mwState.lock()) {
202 if (
auto pAccessState = pState->GetAccessState()) {
203 if (pAccessState->mState.mInitialized)
206 assert(pAccessState->mState.mInitialized);
207 auto& lastSettings = pAccessState->mLastSettings;
209 pAccessState->MainRead();
215 return pAccessState->mLastSettings.settings;
224 if (
auto pState =
mwState.lock()) {
225 if (
auto pAccessState = pState->GetAccessState()) {
226 if (pMessage && !pAccessState->mState.mInitialized) {
229 if (
auto pInstance = pState->mwInstance.lock()) {
230 auto &stateSettings = pState->mMainSettings.settings;
231 stateSettings = std::move(
settings);
233 stateSettings, pMessage.get()
235 pInstance->RealtimeProcessStart(package);
236 pInstance->RealtimeProcessEnd(stateSettings);
237 pAccessState->mLastSettings.
settings = stateSettings;
241 auto &lastSettings = pAccessState->mLastSettings;
243 lastSettings.settings = std::move(
settings);
244 ++lastSettings.counter;
246 pAccessState->MainWrite(
251 void Set(std::unique_ptr<Message> pMessage)
253 if (
auto pState =
mwState.lock()) {
254 if (
auto pAccessState = pState->GetAccessState()) {
255 if (pMessage && !pAccessState->mState.mInitialized) {
258 if (
auto pInstance = pState->mwInstance.lock()) {
259 auto &stateSettings = pState->mMainSettings.settings;
261 stateSettings, pMessage.get()
263 pInstance->RealtimeProcessStart(package);
264 pInstance->RealtimeProcessEnd(stateSettings);
269 auto &lastSettings = pAccessState->mLastSettings;
271 ++lastSettings.counter;
272 pAccessState->MainWrite(
273 lastSettings.counter, std::move(pMessage));
278 if (
auto pState =
mwState.lock()) {
279 if (
auto pAccessState = pState->GetAccessState()) {
280 assert(pAccessState->mMainThreadId == std::this_thread::get_id());
282 if (pAccessState->mState.mInitialized)
284 std::unique_lock lk(pAccessState->mLockForCV);
285 pAccessState->mCV.wait(lk,
287 auto& lastSettings = pAccessState->mLastSettings;
288 pAccessState->MainRead();
289 return pAccessState->mCounter == lastSettings.counter;
298 pState->mMainSettings.Set(pAccessState->mLastSettings);
303 if (
auto pOther =
dynamic_cast<const Access*
>(&other)) {
305 auto &theirs = pOther->mwState;
306 auto less = std::owner_less{};
307 return !(less(mine, theirs) || less(theirs, mine));
328 bool empty =
id.empty();
329 if (
mID.empty() && !empty) {
377std::shared_ptr<EffectInstance>
397 pInstance->SetBlockSize(512);
399 if (!pInstance->RealtimeInitialize(
mMainSettings.settings, sampleRate))
416std::shared_ptr<EffectInstance>
440 unsigned chans,
const unsigned numAudioIn,
const unsigned numAudioOut,
444 for (
unsigned ondx = 0; ondx < chans; ondx += numAudioOut) {
456std::shared_ptr<EffectInstance>
458 const Track &track,
unsigned chans,
float sampleRate)
466 const auto numAudioIn = pInstance->GetAudioInCount();
467 const auto numAudioOut = pInstance->GetAudioOutCount();
469 [&](
unsigned,
unsigned){
471 if (pInstance->RealtimeAddProcessor(
482 mGroups[&track] = { first, sampleRate };
495 pAccessState->WorkerRead();
499 bool active =
IsActive() && running;
502 bool success = active
503 ? pInstance->RealtimeResume()
504 : pInstance->RealtimeSuspend();
520 result = pInstance->RealtimeProcessStart(package);
523 if (!pInstance || !active)
529#define stackAllocate(T, count) static_cast<T*>(alloca(count * sizeof(T)))
534 const float *
const *inbuf,
float *
const *outbuf,
float *
const dummybuf,
540 for (
size_t ii = 0; ii < chans; ++ii)
541 memcpy(outbuf[ii], inbuf[ii], numSamples *
sizeof(
float));
544 const auto numAudioIn = pInstance->GetAudioInCount();
545 const auto numAudioOut = pInstance->GetAudioOutCount();
546 const auto clientIn =
stackAllocate(
const float *, numAudioIn);
549 const auto &pair =
mGroups[&track];
550 auto processor = pair.first;
553 [&](
unsigned indx,
unsigned ondx){
555 unsigned copied =
std::min(chans - indx, numAudioIn);
556 std::copy(inbuf + indx, inbuf + indx + copied, clientIn);
559 while (
auto need = numAudioIn - copied) {
560 auto moreCopied =
std::min(chans, need);
561 std::copy(inbuf, inbuf + moreCopied, clientIn + copied);
562 copied += moreCopied;
566 copied =
std::min(chans - ondx, numAudioOut);
567 std::copy(outbuf + ondx, outbuf + ondx + copied, clientOut);
568 if (copied < numAudioOut) {
570 std::fill(clientOut + copied, clientOut + numAudioOut, dummybuf);
574 const auto blockSize = pInstance->GetBlockSize();
575 for (
size_t block = 0; block < numSamples; block += blockSize) {
576 auto cnt =
std::min(numSamples - block, blockSize);
578 auto processed = pInstance->RealtimeProcess(processor,
585 for (
size_t i = 0 ; i < numAudioIn; i++)
588 for (
size_t i = 0 ; i < numAudioOut; i++)
605 return numSamples - len;
611 bool result = pInstance &&
622 pAccessState->WorkerWrite();
660 if (!pInstance->UsesMessages()) {
665 auto result = pInstance->RealtimeFinalize(
mMainSettings.settings);
673 static const std::string result{
"effect"};
692 for (
auto &[attr, value] : attrs) {
694 SetID(value.ToWString());
713 for (
auto &[attr, value] : attrs) {
715 n = value.ToWString();
717 v = value.ToWString();
750 const auto active =
mMainSettings.settings.extra.GetActive();
763 entryKeepGoing = cmdParms.GetFirstEntry(entryName, entryIndex);
764 while (entryKeepGoing) {
765 wxString entryValue = cmdParms.Read(entryName,
"");
772 entryKeepGoing = cmdParms.GetNextEntry(entryName, entryIndex);
786 return std::make_shared<Access>();
796 return std::make_shared<Access>(*
this);
static const auto parametersAttribute
static const auto versionAttribute
static const auto parameterAttribute
static const auto nameAttribute
static const auto idAttribute
static const auto valueAttribute
static constexpr auto activeAttribute
#define stackAllocate(T, count)
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
static Settings & settings()
std::vector< Attribute > AttributesList
void BuildAll()
For each RegisteredFactory, if the corresponding attachment is absent in this, build and store it.
CommandParameters, derived from wxFileConfig, is essentially doing the same things as the SettingsVis...
virtual wxString GetVersion() const =0
virtual std::shared_ptr< EffectInstance > MakeInstance() const =0
Make an object maintaining short-term state of an Effect.
Hold values to send to effect output meters.
virtual std::unique_ptr< EffectOutputs > Clone() const =0
virtual void Assign(EffectOutputs &&src)=0
Update one Outputs object from another.
Type of messages to send from main thread to processing.
EffectSettingsManager is an EffectDefinitionInterface that adds a factory function for EffectSettings...
virtual bool LoadSettings(const CommandParameters &parms, EffectSettings &settings) const =0
Restore settings from keys and values.
virtual bool SaveSettings(const EffectSettings &settings, CommandParameters &parms) const =0
Store settings as keys and values.
virtual bool CopySettingsContents(const EffectSettings &src, EffectSettings &dst) const
Update one settings object from another.
virtual std::unique_ptr< EffectOutputs > MakeOutputs() const
Produce an object to hold values to send to effect output meters.
virtual EffectSettings MakeSettings() const
static result_type Call(Arguments &&...arguments)
Null check of the installed function is done for you.
Communicate data atomically from one writer thread to one reader.
CallbackReturn Publish(const RealtimeEffectStateChange &message)
Send a message to connected callbacks.
static PluginID GetID(PluginProvider *provider)
Mediator of two-way inter-thread communication of changes of settings.
void Initialize(const EffectSettings &settings, const EffectInstance::Message *pMessage, const EffectOutputs *pOutputs)
MessageBuffer< ToMainSlot > mChannelToMain
const EffectSettingsManager & mEffect
Response::Counter mCounter
void MainWrite(SettingsAndCounter::Counter counter, std::unique_ptr< EffectInstance::Message > pMessage)
RealtimeEffectState & mState
SettingsAndCounter mLastSettings
std::condition_variable mCV
AccessState(const EffectSettingsManager &effect, RealtimeEffectState &state)
MessageBuffer< FromMainSlot > mChannelFromMain
void MainWrite(SettingsAndCounter &&settings, std::unique_ptr< EffectInstance::Message > pMessage)
std::thread::id mMainThreadId
void SetActive(bool active)
Set only in the main thread.
const EffectInstanceFactory * mPlugin
Stateless effect object.
NonInterfering< SettingsAndCounter > mWorkerSettings
NonInterfering< SettingsAndCounter > mMainSettings
Updated immediately by Access::Set in the main thread.
std::optional< EffectInstance::SampleCount > mLatency
How many samples must be discarded.
void HandleXMLEndTag(const std::string_view &tag) override
std::shared_ptr< EffectInstance > GetInstance()
Expose a pointer to the state's instance (making one as needed).
std::shared_ptr< EffectInstance > AddTrack(const Track &track, unsigned chans, float sampleRate)
Main thread sets up this state before adding it to lists.
std::shared_ptr< EffectSettingsAccess > GetAccess()
std::unique_ptr< EffectOutputs > mOutputs
bool ProcessEnd()
Worker thread finishes a batch of samples.
AccessState * GetAccessState() const
AccessState * TestAccessState() const
static const std::string & XMLTag()
bool Finalize() noexcept
Main thread cleans up playback.
std::shared_ptr< EffectInstance > EnsureInstance(double rate)
bool ProcessStart(bool running)
Worker thread begins a batch of samples.
bool IsEnabled() const noexcept
Test only in the main thread.
const PluginID & GetID() const noexcept
bool mLastActive
Assigned in the worker thread at the start of each processing scope.
size_t Process(const Track &track, unsigned chans, const float *const *inbuf, float *const *outbuf, float *dummybuf, size_t numSamples)
Worker thread processes part of a batch of samples.
void WriteXML(XMLWriter &xmlFile)
RealtimeEffectState(const PluginID &id)
bool IsActive() const noexcept
Test only in the worker thread, or else when there is no processing.
std::shared_ptr< EffectInstance > Initialize(double rate)
Main thread sets up for playback.
std::shared_ptr< EffectInstance > MakeInstance()
std::unique_ptr< EffectInstance::Message > mMessage
const EffectInstanceFactory * GetEffect()
Initializes the effect on demand.
std::unordered_map< const Track *, std::pair< size_t, double > > mGroups
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
void SetID(const PluginID &id)
May be called with nonempty id at most once in the lifetime of a state.
std::unique_ptr< EffectInstance::Message > mMovedMessage
std::weak_ptr< EffectInstance > mwInstance
Stateful instance made by the plug-in.
AtomicUniquePointer< AccessState > mpAccessState
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
std::unique_ptr< EffectOutputs > mMovedOutputs
Abstract base class for an object holding data associated with points on a time axis.
This class is an interface which should be implemented by classes which wish to be able to load and s...
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
virtual void StartTag(const wxString &name)
void WriteAttr(const wxString &name, const Identifier &value)
virtual void EndTag(const wxString &name)
void AllocateChannelsToProcessors(unsigned chans, const unsigned numAudioIn, const unsigned numAudioOut, const F &f)
EffectSettings & settings
Externalized state of a plug-in.
Main thread's interface to inter-thread communication of changes of settings.
const EffectSettings & Get() override
void Set(std::unique_ptr< Message > pMessage) override
Message-only overload of Set(). In future, this should be the only one.
Access(RealtimeEffectState &state)
bool IsSameAs(const EffectSettingsAccess &other) const override
void Flush() override
Make the last Set changes "persistent" in underlying storage.
void Set(EffectSettings &&settings, std::unique_ptr< Message > pMessage) override
~Access() override=default
std::weak_ptr< RealtimeEffectState > mwState
Store no state here but this weak pointer, so IsSameAs isn't lying.
Response::Counter counter
std::unique_ptr< EffectInstance::Message > pMessage
Reader(FromMainSlot &&slot, const EffectSettingsManager &effect, RealtimeEffectState &state)
std::unique_ptr< EffectInstance::Message > pMessage
SettingsAndCounter::Counter counter
FromMainSlot & operator=(FromMainSlot &&)=default
FromMainSlot(const EffectSettings &settings, const EffectInstance::Message *pMessage)
FromMainSlot & operator=(Message &&message)
FromMainSlot & operator=(ShortMessage &&message)
Reader(ToMainSlot &&slot, EffectOutputs *pOutputs, Response::Counter &counter)
ToMainSlot(Response response)
ToMainSlot & operator=(ToMainSlot &&)=default
ToMainSlot & operator=(CounterAndOutputs &&arg)
std::unique_ptr< EffectOutputs > pOutputs