Overview #
The overall class structure looks something like below. We enforce pipewire as the media manager but the plugins although is lv2 now, can change in the future to support more plugin types. Hence, the plugins are abstracted

session-manager #
Session manager is responsible to
- Add/Remove new pw-clients as nodes
- Access stored nodes for params changes
- Link/unlink between multiple nodes/pw-clients
- Save and restore session ( > v1.0)
There would always be utmost 1 Session manager instance

pw-client #
Since we use threaded main loop in our pw_client, all operations must happen within pw_thread_loop_lock() and pw_thread_loop_unlock()
Common static structures #
Following are the common structures that are file-static and share with all the dynamically created clients. It is initialized once at the start of the program during Initialization
pw_thread_loop *looppw_context *contextpw_core *core
During De-initialize, The above allocations are cleared
loopwith pw_thread_loop_stop() and pw_thread_loop_destroy()contextwith pw_context_destroy()corewith pw_core_disconnect()
class details #

pwInitClient
#
Init client initializes the pw-client and wraps the underlying plugin to provide a seamless abstraction to the above layers. The underlying plugin can be any of the supported types (refer Plugin Base and Adding a node for more information).
pwLinkClientPorts
#
Links (a.k.a connects) the Source’s output port to the Destination’s input port. It utilizes the impl APIs and calls pw_context_create_link inside. It needs to be a static function (probably outside the class) as it operates on more than one pw-client
pwUnlinkClientPorts
#
Unlinks (a.k.a disconnects) 2 ports.
- Find the port using pw_impl_node_find_port from the src/dst UUID and src/dst PortIdx. This works because we would have set
port-idas the port-index from the plugin desc - Instead of storing the link structure, we would find it on the go using pw_impl_link_find passing the ports found in the previous step
- Destroy it using pw_impl_link_destroy
~PipewireClient
#
Destruct Pipewire client object and destroy underlying plugin instance and filter object
- Calls base plugin’s destruct function
- Disconnects filter from the main-loop with pw_filter_disconnect()
- Destroys the filter with pw_filter_destroy()
Processing #
Apart from the above global variables, the processing of plugin will be a common callback function. This is because all of the plugins have to perform following functionality in the sequence
- Get ports with pw_filter_get_dsp_buffer(). Do note that this only works as we register the node as a DSP media role
- Connect the ports via Plugin Base and underlying plugin specific mechanism
- Run the plugin instance with pluginRun
During the addition of ports, the port_ID of the pw_client was configured to be the same as the port’s index of the plugin. The same shall be used to connect the ports. Also during the filter creation using pw_filter_new_simple(), an opaque pointer for the user data was passed. This user data is mostly a reference to pluginMgr of the class. The same would be passed to process callback
Plugin Base #
Plugin base is an abstract class which provides the interface to pipewire client class. This helps to interface various plugin types (vst, ladspa, clap…). For the v1.0, we would be supporting only lv2 type plugins
This class would solely be controlled by pw-client. Hence, there is an instance of Plugin base for every instance of pw-client

LV2-manager #
Class responsible to manage lv2 specific operation. I.e,
- Parsing the plugins to fetch plugin description (ports, number and type of controls, metadata etc…)
- Instantiating and un-instantiating a plugin
- Run a plugin instance for every sample

pluginUpdateParam
#
pluginUpdateParam will internally call lilv_instance_connect_port from the lilv library to connect a control port of the current instance to a value and update it, therefore updating the plugin instance’s port value.
pluginDeactivate and pluginDestroy
#
Both are called at the termination of the pw_client. pluginDeactivate for lv2 calls lilv_instance_deactivate() to deactivate the active instance of lv2 plugin and pluginDestroy calls lilv_instance_free() to free the instance’s resources.
pluginConnectPort
#
pluginConnectPort tries to connect the shared buffer to the plugin’s port. The lv2 class override of this function calls lilv_instance_connect_port() internally.
pluginRun
#
Run an instance of the plugin using the call to lilv_instance_run(). The process the input buffer according to the plugin’s DSP and provides it at the output