21#include <condition_variable>
43 pOutputs ? pOutputs->
Clone() :
nullptr } });
45 pOutputs ? pOutputs->
Clone() :
nullptr } });
57 std::unique_ptr<EffectInstance::Message> pMessage) {
63 std::unique_ptr<EffectInstance::Message> pMessage) {
65 counter, std::move(pMessage) });
112 if (pOutputs && slot.mResponse.pOutputs)
113 pOutputs->
Assign(std::move(*slot.mResponse.pOutputs));
114 counter = slot.mResponse.counter;
140 mMessage.SettingsAndCounter::swap(message);
160 if(slot.mMessage.counter ==
settings.counter)
162 settings.counter = slot.mMessage.counter;
166 slot.mMessage.settings,
settings.settings);
167 settings.settings.extra = slot.mMessage.settings.extra;
170 state.
mMovedMessage->Assign(std::move(*slot.mMessage.pMessage));
186 std::condition_variable
mCV;
195 :
mwState{ state.weak_from_this() }
202 if (
auto pState =
mwState.lock()) {
203 if (
auto pAccessState = pState->GetAccessState()) {
204 if (pAccessState->mState.mInitialized)
207 assert(pAccessState->mState.mInitialized);
208 auto& lastSettings = pAccessState->mLastSettings;
210 pAccessState->MainRead();
216 return pAccessState->mLastSettings.settings;
225 if (
auto pState =
mwState.lock()) {
226 if (
auto pAccessState = pState->GetAccessState()) {
227 if (pMessage && !pAccessState->mState.mInitialized) {
230 if (
auto pInstance = pState->mwInstance.lock()) {
231 auto &stateSettings = pState->mMainSettings.settings;
232 stateSettings = std::move(
settings);
234 stateSettings, pMessage.get()
236 pInstance->RealtimeProcessStart(package);
237 pInstance->RealtimeProcessEnd(stateSettings);
238 pAccessState->mLastSettings.
settings = stateSettings;
242 auto &lastSettings = pAccessState->mLastSettings;
244 lastSettings.settings = std::move(
settings);
245 ++lastSettings.counter;
247 pAccessState->MainWrite(
252 void Set(std::unique_ptr<Message> pMessage)
254 if (
auto pState =
mwState.lock()) {
255 if (
auto pAccessState = pState->GetAccessState()) {
256 if (pMessage && !pAccessState->mState.mInitialized) {
259 if (
auto pInstance = pState->mwInstance.lock()) {
260 auto &stateSettings = pState->mMainSettings.settings;
262 stateSettings, pMessage.get()
264 pInstance->RealtimeProcessStart(package);
265 pInstance->RealtimeProcessEnd(stateSettings);
270 auto &lastSettings = pAccessState->mLastSettings;
272 ++lastSettings.counter;
273 pAccessState->MainWrite(
274 lastSettings.counter, std::move(pMessage));
279 if (
auto pState =
mwState.lock()) {
280 if (
auto pAccessState = pState->GetAccessState()) {
281 assert(pAccessState->mMainThreadId == std::this_thread::get_id());
283 if (pAccessState->mState.mInitialized)
285 std::unique_lock lk(pAccessState->mLockForCV);
286 pAccessState->mCV.wait(lk,
288 auto& lastSettings = pAccessState->mLastSettings;
289 pAccessState->MainRead();
290 return pAccessState->mCounter == lastSettings.counter;
299 pState->mMainSettings.Set(pAccessState->mLastSettings);
304 if (
auto pOther =
dynamic_cast<const Access*
>(&other)) {
306 auto &theirs = pOther->mwState;
307 auto less = std::owner_less{};
308 return !(less(mine, theirs) || less(theirs, mine));
329 bool empty =
id.empty();
330 if (
mID.empty() && !empty) {
378std::shared_ptr<EffectInstance>
398 pInstance->SetBlockSize(512);
417std::shared_ptr<EffectInstance>
441 unsigned chans,
const unsigned numAudioIn,
const unsigned numAudioOut,
445 for (
unsigned ondx = 0; ondx < chans; ondx += numAudioOut) {
457std::shared_ptr<EffectInstance>
468 const auto numAudioIn = pInstance->GetAudioInCount();
469 const auto numAudioOut = pInstance->GetAudioOutCount();
471 [&](
unsigned,
unsigned){
473 if (pInstance->RealtimeAddProcessor(
498 pAccessState->WorkerRead();
502 bool active =
IsActive() && running;
505 bool success = active
506 ? pInstance->RealtimeResume()
507 : pInstance->RealtimeSuspend();
523 result = pInstance->RealtimeProcessStart(package);
526 if (!pInstance || !active)
532#define stackAllocate(T, count) static_cast<T*>(alloca(count * sizeof(T)))
538 const float *
const *inbuf,
float *
const *outbuf,
float *
const dummybuf,
544 for (
size_t ii = 0; ii < chans; ++ii)
545 memcpy(outbuf[ii], inbuf[ii], numSamples *
sizeof(
float));
548 const auto numAudioIn = pInstance->GetAudioInCount();
549 const auto numAudioOut = pInstance->GetAudioOutCount();
550 const auto clientIn =
stackAllocate(
const float *, numAudioIn);
553 const auto &pair =
mGroups[&group];
554 auto processor = pair.first;
557 [&](
unsigned indx,
unsigned ondx){
559 unsigned copied =
std::min(chans - indx, numAudioIn);
560 std::copy(inbuf + indx, inbuf + indx + copied, clientIn);
563 while (
auto need = numAudioIn - copied) {
564 auto moreCopied =
std::min(chans, need);
565 std::copy(inbuf, inbuf + moreCopied, clientIn + copied);
566 copied += moreCopied;
570 copied =
std::min(chans - ondx, numAudioOut);
571 std::copy(outbuf + ondx, outbuf + ondx + copied, clientOut);
572 if (copied < numAudioOut) {
574 std::fill(clientOut + copied, clientOut + numAudioOut, dummybuf);
578 const auto blockSize = pInstance->GetBlockSize();
579 for (
size_t block = 0; block < numSamples; block += blockSize) {
580 auto cnt =
std::min(numSamples - block, blockSize);
582 auto processed = pInstance->RealtimeProcess(processor,
589 for (
size_t i = 0 ; i < numAudioIn; i++)
592 for (
size_t i = 0 ; i < numAudioOut; i++)
609 return numSamples - len;
615 bool result = pInstance &&
626 pAccessState->WorkerWrite();
664 if (!pInstance->UsesMessages()) {
669 auto result = pInstance->RealtimeFinalize(
mMainSettings.settings);
677 static const std::string result{
"effect"};
696 for (
auto &[attr, value] : attrs) {
698 SetID(value.ToWString());
717 for (
auto &[attr, value] : attrs) {
719 n = value.ToWString();
721 v = value.ToWString();
754 const auto active =
mMainSettings.settings.extra.GetActive();
767 entryKeepGoing = cmdParms.GetFirstEntry(entryName, entryIndex);
768 while (entryKeepGoing) {
769 wxString entryValue = cmdParms.Read(entryName,
"");
776 entryKeepGoing = cmdParms.GetNextEntry(entryName, entryIndex);
790 return std::make_shared<Access>();
800 return std::make_shared<Access>(*
this);
Abstract class ChannelGroup with two discrete iterable dimensions, channels and intervals; subclasses...
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
virtual bool IsLeader() const =0
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
size_t Process(const ChannelGroup &group, unsigned chans, const float *const *inbuf, float *const *outbuf, float *dummybuf, size_t numSamples)
Worker thread processes part of a batch of samples.
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 > AddGroup(const ChannelGroup &group, unsigned chans, float sampleRate)
Main thread sets up this state before adding it to lists.
std::unordered_map< const ChannelGroup *, std::pair< size_t, double > > mGroups
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.
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.
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
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)
constexpr auto sampleRate
void AllocateChannelsToProcessors(unsigned chans, const unsigned numAudioIn, const unsigned numAudioOut, const F &f)
void copy(const T *src, T *dst, int32_t n)
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