-
Notifications
You must be signed in to change notification settings - Fork 2
AudYoFlo: Class Templates: Extending CjvxBareNode1ioX Classes with Sub Chains
Often, a component may involve another components functionality. This procedure is denoted as component nesting. More generally, component nesting describes a situation where a chain of components is included as part of another component. This is shown in the following diagram:
This section of the documentation describes how to realize the "integrating component" in the Figure and how to specify the "integrated components", "Component 1", "Component 2" and "Component 3". The idea to combine components within another component, a component chain nesting, is of particular interest when using complex data flows in the AudYoFlo system as well as using functions in another environment such as, e.g., a VST host.
There are two options how to integrate the sub chain of components: the integration may be realized as a fixed connection or as a flexible connection. The fixed connection is the more efficient approach to integrate the chained components since it does not lead to any computational overhead. In contrast to this, the flexible connection is the more flexible approach: the inner chain of the integrated components can be established and deallocated while the component is already running. On the other hand, the flexibility comes to the price of a little more computationally complexity as memory buffers of the signal must be copied on the input and the output side.
Both implementations of the connection are located in the library sources/jvxLibraries/ayf-node-connections
. In order to use it, the include directories
${JVX_SUBPRODUCT_LIBS_INCLUDE_PATH}/ayf-node-connections/include
${JVX_SUBPRODUCT_BINARY_INCLUDE_PATH}/ayf-node-connections/generated
must be be added in the CMakeLists.txt
file. The functionality is mainly in the header files as both classes are template classes. However, part of it is also pre-compiled and must therefore be linked as a library by adding
ayf-node-connections_static
to the LOCAL_LIBS
variable in the CMakeLists.txt
file.
The header are include by means of the specifications
#include "CayfAuNFixedConnection.h"
or
#include "CayfAuNFlexibleConnection.h"
Principally, a fixed connection sub chain is fixed, which means that the chain can not be disengaged or re-engaged while the integrating component is in processing state. It can, however, be reconfigured if the component is not processing. Hence, the integrating component must be setup for integrated components in ACTIVE state and will be operated for data processing when in state PROCESSING. In terms of signal processing complexity, the fixed connection acts as efficiently as a native chain in the AudYoFlo system. Indeed, from the dataflow graph principle, the fixed connection is identical to a native chain as shown in the following Figure:
Principally, the sub chaining simplfies interacting from the outside: From the host point of view, a simple chain is established with a device connecting to and from a single component only. In the dataflow graph, however, due to the inner signal processing chain, multiple components are chained and involved. And while the outer connection is fixed (device-component-device-connection) the internal connection may be subject to change (as long as the changes happen in the state ACTIVE).
In order to create the "integrating component", a new class must be implemented on the basis of the base class CayfAuNSubChain<AyfConnection::CayfAuNFixedConnection<CjvxBareNode1ioRearrange> >
or AyfConnection::CayfAuNFixedConnection<CjvxBareNode1ioRearrange>
. In the following we will run the template CayfAuNFixedConnection
on the class CjvxBareNode1ioRearrange
. However, the mechanism also works with class CjvxBareNode1io_zerocopy
due to the parameterization as a template.
#include "CjvxAuNSubChain.h"
#include "jvxNodes/CjvxBareNode1ioRearrange.h"
#include "CayfAuNFixedConnection.h"
Then, the new class is declared as
class CayfAuNMyNewClass :
public CayfAuNSubChain<AyfConnection::CayfAuNFixedConnection<CjvxBareNode1ioRearrange> >
{
...
}
This class can be realized with all hints and tricks as described in the documentation for the base class CjvxBareNode1ioRearrange
. However, the sub chain may be involved as desired when processing is performed.
In the configuration phase - that is before the chain is connected -, the components to be integrated can be specified by calling the member function
jvxErrorType CayfAuNConnection::attach_component_chain(
const char* moduleName, jvxComponentType tp,
jvxBool activateBeforeAttach = false, jvxSize* uniqueId = nullptr,
IjvxReferenceSelector* deciderArg = nullptr) override;
which is hence part of the base class we derived from. In the call of the member function, the name of the component module is specified in parameter moduleName
and the type in tp
if another component in the component grid of the host environment (Type, SlotId, SubSlotId).
This call at this point only provides the description of the component to be integrated. The concatenation will be active later as we will see in the following. However, when the component is integrated, it may already be in state ACTIVE or it may also be activated when being integrated.
With the parameter activateBeforeAttach
set to true
the next slot for component of type tp
will be activated and integrated in the context of the grid location of all components. If activateBeforeAttached
is false, the additional argument deciderArg
can be provided to decide which component is integrated. For example, the first component which is not connected can be selected. If this argument is a nullptr
, the first component in state ACTIVE is selected.
A unique id can be returned in case mulitple components from the same module are involved. This unique id is required to detach the integrated components lateron.
Alternative to the specification of the integrated component via location, another member function can be used to provide a component by means of a pointer reference in the call
jvxErrorType CayfAuNConnection::attach_component_chain(
const char* moduleName, IjvxObject* theObj,
jvxSize* uniqueId = nullptr) override;
Here, the argument moduleName
is used to identify the integrated component. The actual component is given as pointer theObj
which should be a component in ACTIVE state when calling the function. A unique_id
can be returned if the same moduleName
ist used for different attached components.
The call to attach the components may be called from within the new component. In this case, it is sufficient to derive our class from base class AyfConnection::CayfAuNFixedConnection<CjvxBareNode1ioRearrange>
. If it is desired that the components shall be specified from outside our integrating component, there is an additional property /iface/ex_interface
which can be adressed through the property interface, e.g.,
IjvxPropertyExtender* pExt = nullptr;
jvx::propertyRawPointerType::CjvxRawPointerTypeExternalPointer rPtr(&pExt);
jvxErrorType resL = propRef->get_property(jvxCallManagerProperties(), rPtr,
jPAD("/iface/ex_interface"), jPD(true));
if (pExt)
{
IjvxPropertyExtenderChainControl* propExtSpec =
castPropIfExtender< IjvxPropertyExtenderChainControl>(pExt);
if (propExtSpec)
{
propExtSpec->attach_component_chain("ayfAuNComponent1", JVX_COMPONENT_AUDIO_NODE, false);
propExtSpec->attach_component_chain("ayfAuNComponent2", JVX_COMPONENT_AUDIO_NODE, false);
propExtSpec->attach_component_chain("ayfAuNComponent3", JVX_COMPONENT_AUDIO_NODE, false);
propExtSpec->attach_component_chain("ayfAuNComponent4", JVX_COMPONENT_AUDIO_NODE, false);
}
}
The interface IjvxPropertyExtenderChainControl
is defined as follows:
JVX_INTERFACE IjvxPropertyExtenderChainControl
{
public:
virtual ~IjvxPropertyExtenderChainControl() {};
virtual jvxErrorType JVX_CALLINGCONVENTION set_stateswitch_node_handler(IayfConnectionStateSwitchNode* ssCb) = 0;
virtual jvxErrorType JVX_CALLINGCONVENTION attach_component_chain(
const char* str, jvxComponentType tp = JVX_COMPONENT_AUDIO_NODE,
jvxBool activateBeforeAttach = false, jvxSize* uniqueId = nullptr,
IjvxReferenceSelector* deciderArg = nullptr) = 0;
virtual jvxErrorType JVX_CALLINGCONVENTION attach_component_chain(
const char* str, IjvxObject* theObj,
jvxSize* uniqueId = nullptr) = 0;
virtual jvxErrorType JVX_CALLINGCONVENTION detach_component_chain(
const char* str, jvxSize uniqueId = JVX_SIZE_UNSELECTED) = 0;
};
This option can be activated by using the class CayfAuNSubChain<AyfConnection::CayfAuNFixedConnection<CjvxBareNode1ioRearrange> >
as the base class.
Principally, the specified integrated components are linked in a linear chain in the order as specified.
The inverse to the specification of the components is typically done once the integrating component is not longer linker. This can safely be done in state UNSELECTED and can be realized as follows:
IjvxPropertyExtender* pExt = nullptr;
jvx::propertyRawPointerType::CjvxRawPointerTypeExternalPointer<IjvxPropertyExtender> rPtr(&pExt);
jvxErrorType resL = propRef->get_property(jvxCallManagerProperties(),
rPtr, jPAD("/iface/ex_interface"), jPD(true));
if (pExt)
{
IjvxPropertyExtenderChainControl* propExtSpec =
castPropIfExtender< IjvxPropertyExtenderChainControl>(pExt);
if (propExtSpec)
{
propExtSpec->detach_component_chain("ayfAuNComponent1", JVX_SIZE_UNSELECTED);
propExtSpec->detach_component_chain("ayfAuNComponent2", JVX_SIZE_UNSELECTED);
propExtSpec->detach_component_chain("ayfAuNComponent3", JVX_SIZE_UNSELECTED);
propExtSpec->detach_component_chain("ayfAuNComponent4", JVX_SIZE_UNSELECTED);
}
}
In order to involve the mechanism to engage components is very simple. The following steps are required:
- Derive your class from
AyfConnection::CayfAuNFixedConnection<CjvxBareNode1ioRearrange>
orCayfAuNSubChain<AyfConnection::CayfAuNFixedConnection<CjvxBareNode1ioRearrange> >
. The leadingCayfAuNSubChain
is always required if you want to specify the involved components via an interface pointer reference. - If you use the variant with the leading
CayfAuNSubChain
, you need to specify the components to be involved BEFORE the first call of the test function is called. A good moment is, e.g., when your class has been selected. The components are involved by calling one of theattach_component_chain
member functions in theIjvxPropertyExtenderChainControl
interface. - Then, the components are engaged in the
test_connection_icon
functions within theAyfConnection::CayfAuNFixedConnection
implementation. If a specific behavior is required such as dynamic component involvement, you can redefine the implementation of thetest_connection_icon
function.
When implementing
The class which is derived from the class template may provide callbacks related to the IayfConnectionStateSwitchNode
interface:
JVX_INTERFACE IayfConnectionStateSwitchNode
{
virtual jvxErrorType runStateSwitch(jvxStateSwitch ss, IjvxNode* node, const char* moduleName, IjvxObject* theOwner = nullptr) = 0;
virtual jvxErrorType componentsAboutToConnect() = 0;
virtual jvxErrorType runTestChainComplete(jvxErrorType lastResult, IjvxNode* node, const char* moduleName, jvxSize uniqueId) = 0;
};
The member functions are called in the following situations:
- Function
IayfConnectionStateSwitchNode::runStateSwitch
: This function is called before a component state switch. In the callback, the component must be controlled to change the state. For example, on state switch JVX_STATE_SWITCH_SELECT the componentmust be selected. The caller of the callback does NOT run the select itself unless this function is implemented to return error codeJVX_ERROR_UNSUPPORTED
. - Function
IayfConnectionStateSwitchNode::componentsAboutToConnect
: This function is called right before the chain is setup by connecting the components. At this moment, all components are in state ACTIVE. The developer may take the opportunity to set properties within this hook to align the functionality of all involved components. - Function
IayfConnectionStateSwitchNode::runTestChainComplete
: This function is called on every test run of the micro connection. With every test run the processing parameters may have changed and may yield a specific action for automation. The test should never be run if the components are in state PROCESSING. The developer may, e.g., take the opportunity to modify the setup if the test function fails. The argumentlastResult
provides the status of the last test run, thenode
pointer, themoduleName
and theuniqueId
may be used to identify single components. This function is called for every registered node.