Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ namespace nap

mMeter = nodeManager.makeSafe<LevelMeterNode>(nodeManager, mResource->mAnalysisWindowSize);
mMeter->setType(mResource->mMeterType);
nodeManager.registerRootProcess(mMeter.get());

if (mResource->mFilterInput)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@ namespace nap
// If the channel is out of bounds we create a PullNode instead of an OutputNode in order to process the connected DSP branch.
auto pullNode = nodeManager.makeSafe<PullNode>(nodeManager);
pullNode->audioInput.connect(*mInput->getOutputForChannel(mChannelRouting[channel]));
nodeManager.registerRootProcess(pullNode.get());
mOutputs.emplace_back(std::move(pullNode));
continue;
}
else {
auto outputNode = nodeManager.makeSafe<OutputNode>(nodeManager);
outputNode->setOutputChannel(channel);
outputNode->audioInput.connect(*mInput->getOutputForChannel(mChannelRouting[channel]));
nodeManager.registerRootProcess(outputNode.get());
mOutputs.emplace_back(std::move(outputNode));
}
}
Expand Down
51 changes: 36 additions & 15 deletions system_modules/napaudio/src/audio/core/audionodemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ namespace nap

{
for (auto& root : mRootProcesses)
root->process();
if (root != nullptr)
root->update();
}

for (auto channel = 0; channel < mOutputChannelCount; ++channel) {
Expand Down Expand Up @@ -101,7 +102,8 @@ namespace nap

{
for (auto& root : mRootProcesses)
root->update();
if (root != nullptr)
root->update();
}

for (auto channel = 0; channel < mOutputChannelCount; ++channel) {
Expand Down Expand Up @@ -162,40 +164,59 @@ namespace nap
}


void NodeManager::registerProcess(Process& process)
void NodeManager::registerProcess(SafePtr<Process> process)
{
process.setSampleRate(mSampleRate);
process.setBufferSize(mInternalBufferSize);
process->setSampleRate(mSampleRate);
process->setBufferSize(mInternalBufferSize);
auto oldSampleRate = mSampleRate;
auto oldBufferSize = mInternalBufferSize;
enqueueTask([&, oldSampleRate, oldBufferSize]() {

enqueueTask([&, oldSampleRate, oldBufferSize, process]() {
// Check if the Process is not (being) deleted
if (process == nullptr)
return;

// In the extremely rare case the buffersize or the samplerate of the node manager have been changed in between the enqueueing of the task and its execution on the audio thread, we set them again.
// However we prefer not to, in order to avoid memory allocation on the audio thread.
if (oldSampleRate != mSampleRate)
process.setSampleRate(mSampleRate);
process->setSampleRate(mSampleRate);
if (oldBufferSize != mInternalBufferSize)
process.setBufferSize(mInternalBufferSize);
process.mRegisteredWithNodeManager.store(true);
mProcesses.emplace(&process);
process->setBufferSize(mInternalBufferSize);
process->mRegisteredWithNodeManager.store(true);
mProcesses.emplace_back(process.get());
});
}


void NodeManager::unregisterProcess(Process& process)
{
mProcesses.erase(&process);
auto it = std::find_if(mProcesses.begin(), mProcesses.end(), [&](auto& el){ return el == &process; });
if (it != mProcesses.end())
mProcesses.erase(it);
}


void NodeManager::registerRootProcess(Process& rootProcess)
void NodeManager::registerRootProcess(SafePtr<Process> rootProcess)
{
enqueueTask([&]() { mRootProcesses.emplace(&rootProcess); });
enqueueTask([&, rootProcess]()
{
if (rootProcess == nullptr)
return;
mRootProcesses.emplace_back(rootProcess);
});
}


void NodeManager::unregisterRootProcess(Process& rootProcess)
void NodeManager::unregisterRootProcess(SafePtr<Process> rootProcess)
{
mRootProcesses.erase(&rootProcess);
enqueueTask([&, rootProcess]()
{
if (rootProcess == nullptr)
return;
auto it = std::find_if(mRootProcesses.begin(), mRootProcesses.end(), [&](auto &el){ return el.get() == rootProcess.get(); });
if (it != mRootProcesses.end())
mRootProcesses.erase(it);
});
}


Expand Down
33 changes: 26 additions & 7 deletions system_modules/napaudio/src/audio/core/audionodemanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,19 +136,20 @@ namespace nap
void setInternalBufferSize(int size);

/**
* Used by nodes to register themselves to be processed directly by the node manager
* Use this to register a Process as root process. Called from control thread.
* @param rootProcess The root process is a process or node that is executed on every audio callback without being connected to an input of another node.
* In most cases the root process is an OutputNode.
*/
void registerRootProcess(Process& rootProcess);
void registerRootProcess(SafePtr<Process> rootProcess);


/**
* Used by nodes to unregister themselves to be processed directly by the node manager.
* Use this to manually unregister a Process as root process. Called from control thread.
* Note: Processes also unregister themselves as root process automatically from their destructors.
* @param rootProcess The root process is a process or node that is executed on every audio callback without being connected to an input of another node.
* In most cases the root process is an OutputNode.
*/
void unregisterRootProcess(Process& rootProcess);
void unregisterRootProcess(SafePtr<Process> rootProcess);

/**
* Constructs an object managed by a SafeOwner that will dispose the object in the NodeManager's DeletionQueue when it is no longer used.
Expand All @@ -161,6 +162,15 @@ namespace nap
SafeOwner<T> makeSafe(Args&& ... args)
{
auto owner = SafeOwner<T>(mDeletionQueue, new T(std::forward<Args>(args)...));

// If the newly constructed object is a Process, set its internal SafePtr to itself, and register it.
Process* process = rtti_cast<Process>(owner.getRaw());
if (process != nullptr)
{
process->mSelf = owner.get();
registerProcess(owner.get());
}

return owner;
}

Expand All @@ -175,6 +185,15 @@ namespace nap
SafeOwner<T> makeSafe(T* ptr)
{
auto owner = SafeOwner<T>(mDeletionQueue, ptr);

// If the newly constructed object is a Process, set its internal SafePtr to itself, and register it.
Process* process = rtti_cast<Process>(owner.getRaw());
if (process != nullptr)
{
process->mSelf = owner.get();
registerProcess(owner.get());
}

return owner;
}

Expand All @@ -191,7 +210,7 @@ namespace nap

private:
// Used by the nodes and audio processes to register themselves on construction
void registerProcess(Process& process);
void registerProcess(SafePtr<Process> process);

// Used by the nodes and audio processes to unregister themselves on destruction
void unregisterProcess(Process& process);
Expand Down Expand Up @@ -230,8 +249,8 @@ namespace nap

std::vector<float*> mInputBuffer; // Pointing to the audio input that this node manager has to process. The format is a non-interleaved array containing a float array for each channel.

std::set<Process*> mProcesses; // all the audio processes managed by this node manager
std::set<Process*> mRootProcesses; // the nodes that will be processed directly by the manager on every audio callback
std::vector<Process*> mProcesses; // all the audio processes managed by this node manager
std::vector<SafePtr<Process>> mRootProcesses; // the nodes that will be processed directly by the manager on every audio callback

nap::TaskQueue mTaskQueue = { 256 }; // Queue with lambda functions to be executed before processing the next internal buffer.
DeletionQueue& mDeletionQueue; // Deletion queue used to safely create and destruct nodes in a threadsafe manner.
Expand Down
Loading