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>
467 const auto numAudioIn = pInstance->GetAudioInCount();
468 const auto numAudioOut = pInstance->GetAudioOutCount();
470 [&](
unsigned,
unsigned){
472 if (pInstance->RealtimeAddProcessor(
497 pAccessState->WorkerRead();
501 bool active =
IsActive() && running;
504 bool success = active
505 ? pInstance->RealtimeResume()
506 : pInstance->RealtimeSuspend();
522 result = pInstance->RealtimeProcessStart(package);
525 if (!pInstance || !active)
531#define stackAllocate(T, count) static_cast<T*>(alloca(count * sizeof(T)))
537 const float *
const *inbuf,
float *
const *outbuf,
float *
const dummybuf,
542 const auto& pair =
mGroups[group];
543 const float**
const clientIn =
544 pInstance ?
stackAllocate(
const float*, pInstance->GetAudioInCount()) :
547 const auto PointAtCorrectInputBuffers =
548 [&](
unsigned numAudioIn,
unsigned indx) {
550 unsigned copied =
std::min(chans - indx, numAudioIn);
551 std::copy(inbuf + indx, inbuf + indx + copied, clientIn);
554 while (
auto need = numAudioIn - copied)
556 auto moreCopied =
std::min(chans, need);
557 std::copy(inbuf, inbuf + moreCopied, clientIn + copied);
558 copied += moreCopied;
565 for (
size_t ii = 0; ii < chans; ++ii)
566 memcpy(outbuf[ii], inbuf[ii], numSamples *
sizeof(
float));
569 auto processor = pair.first;
570 const auto numAudioIn = pInstance->GetAudioInCount();
571 const auto numAudioOut = pInstance->GetAudioOutCount();
573 chans, numAudioIn, numAudioOut, [&](
unsigned indx,
unsigned ondx) {
575 PointAtCorrectInputBuffers(numAudioIn, indx);
578 const auto blockSize = pInstance->GetBlockSize();
579 for (
size_t block = 0; block < numSamples; block += blockSize)
581 auto cnt =
std::min(numSamples - block, blockSize);
582 pInstance->RealtimePassThrough(
584 for (
size_t i = 0; i < numAudioIn; i++)
594 const auto numAudioIn = pInstance->GetAudioInCount();
595 const auto numAudioOut = pInstance->GetAudioOutCount();
598 auto processor = pair.first;
601 chans, numAudioIn, numAudioOut, [&](
unsigned indx,
unsigned ondx) {
603 PointAtCorrectInputBuffers(numAudioIn, indx);
606 unsigned copied =
std::min(chans - ondx, numAudioOut);
607 std::copy(outbuf + ondx, outbuf + ondx + copied, clientOut);
608 if (copied < numAudioOut)
610 std::fill(clientOut + copied, clientOut + numAudioOut, dummybuf);
613 const auto blockSize = pInstance->GetBlockSize();
614 for (
size_t block = 0; block < numSamples; block += blockSize)
616 auto cnt =
std::min(numSamples - block, blockSize);
618 auto processed = pInstance->RealtimeProcess(
625 for (
size_t i = 0; i < numAudioIn; i++)
628 for (
size_t i = 0; i < numAudioOut; i++)
646 return numSamples - len;
652 bool result = pInstance &&
663 pAccessState->WorkerWrite();
701 if (!pInstance->UsesMessages()) {
706 auto result = pInstance->RealtimeFinalize(
mMainSettings.settings);
714 static const std::string result{
"effect"};
733 for (
auto &[attr, value] : attrs) {
735 SetID(value.ToWString());
754 for (
auto &[attr, value] : attrs) {
756 n = value.ToWString();
758 v = value.ToWString();
791 const auto active =
mMainSettings.settings.extra.GetActive();
804 entryKeepGoing = cmdParms.GetFirstEntry(entryName, entryIndex);
805 while (entryKeepGoing) {
806 wxString entryValue = cmdParms.Read(entryName,
"");
813 entryKeepGoing = cmdParms.GetNextEntry(entryName, entryIndex);
827 return std::make_shared<Access>();
837 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
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(const 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.
std::shared_ptr< EffectInstance > AddGroup(const ChannelGroup *group, unsigned chans, float sampleRate)
Main thread sets up this state before adding it to lists.
NonInterfering< SettingsAndCounter > mWorkerSettings
NonInterfering< SettingsAndCounter > mMainSettings
Updated immediately by Access::Set in the main thread.
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.
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::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