This section describes the software architecture of the platform. The design relies on the concept of box algorithm which ensures maximum flexibility and reusability. The platform is composed of a kernel and several plug-ins dedicated to specific tasks.
The box algorithm
The box algorithm is a key component of the platform. It consists of a “black box” in charge of a fraction of the whole processing pipeline, which exposes inputs and outputs to other box algorithms. Boxes are notified on clock ticks and upon input data and message arrival. The behavior of a box can be adapted to the needs of each algorithm (for instance, acquisition algorithms typically react to clock signals whereas processing algorithms typically react to input arrival). The characteristics and constraints that are common to all box algorithms include reasonable granularity to allow quick software components rearrangement. Newly developed box algorithms are immediately available to the user thanks to the plugin system (see section The plug-ins).
The kernel (see OpenViBE::Kernel::IKernelContext) provides global services to applications through several managers, each of them providing a set of specialized services. Virtually any manager can be added to the kernel in order to extend its services. The most significant managers of the platform are:
The scenario manager (see OpenViBE::Kernel::IScenarioManager) helps creating and configuring scenarios. After a scenario is created, it can instantiate new box algorithms, change their settings and connect boxes together using communication links. The scenario manager is designed to edit any number of scenarios at once, allowing an application to handle multiple scenarios simultaneously. The designer authoring tool takes advantage of this.
The player manager (see OpenViBE::Kernel::IPlayerManager) provides an easy to use interface in order to build and configure a runtime session. For this, the manager handles a collection of players, each one managing several boxes, as described in the scenario it has to run. Each of these box rely on a plug-in box algorithm to perform its tasks.
The visualization manager (see OpenViBE::Kernel::IVisualisationManager) is responsible for displaying 2D or 3D graphical information and dispatching it in the correct place. Indeed, multiple visualization windows may be used. The windows arrangement in space is done by the visualization manager at editing time, thanks to the designer application, and saved to a file. Basic signal display windows are provided with a 2D rendering context (see Figure fig-display-widgets), while more advanced rendering is performed thanks to the 3D library encapsulated in the player module (see section openvibe-vr).
The type manager (see OpenViBE::Kernel::ITypeManager) ensures coherency of all data types. Two kinds of data are manipulated in the platform: simple parameters used to configure boxes, and streams used to connect boxes together and send buffers between boxes. The manager provides a list of registered types, handles conversions and provides information about type compatibility. Simple parameters include integer values, floating point values, strings and filenames. The manager also supports enumerations or bit masks. The stream type tells the scenario editor which box output can be connected to which box input. Stream types are organized hierarchically, allowing to easily downcast some of the streams. For example, an n-electrode raw record will most probably use the signal stream type. This stream type is a specialization of the basic matrix stream type. Thus, each box algorithm working on basic matrix streams will be able to work on raw signal streams.
The plug-in manager (see OpenViBE::Kernel::IPluginManager) makes the platform extensible. This manager is able to dynamically load plug-in modules (e.g., .DLL files under Windows, or .so files under Linux) and collect plug-in object descriptors from them. Then, using these plug-in object descriptors, it provides the service of building new specialized plug-in objects, acting as an object factory for the whole platform. Specialized plug-in objects include scenario serializers, algorithms and box algorithms (see section The plug-ins).
The plug-in system allows to quickly and efficiently expand functionalities. The communication interface between plug-in objects and the platform is defined so that these external objects can be shared, integrated to the platform and replaced when needed.
Our platform includes three different plug-in families:
The algorithm plug-ins (see OpenViBE::Plugins::IAlgorithm and OpenViBE::Plugins::IAlgorithmDesc) are a generic abstraction for any extension that could be added to the platform (e.g., add a new feature extraction or signal processing methods). Only developers can work with this kind of object. Algorithms are the developer’s atomic objects. The developer may compose several algorithms in order to achieve a complex task. This kind of plugin allows to massively share and reuse software components, even in an offline context where time is managed differently (e.g., EEG file reading or signal visualisation widgets).
The box-algorithm plug-ins (see OpenViBE::Plugins::IBoxAlgorithm and OpenViBE::Plugins::IBoxAlgorithmDesc) are the software components each box relies on. Box algorithms are the author’s atomic objects. The developer describes them in a simple structure that notably contains the box prototype (its name, input/output connectors and settings). The box algorithm itself is responsible for the actual processing, i.e., it reads from inputs, computes data to produce a result and writes to outputs. However, the box algorithm generally combines several algorithm plug-ins together to perform this processing. This ensures fast development thanks to the re-usability of components (e.g., most box algorithms use a specific algorithm in order to easily read from inputs and write to outputs). Thus, the box algorithm can be seen as a set of callbacks called periodically by the player. The most important callbacks are the three notification callbacks (clock tick, input arrival and message arrival) and the actual processing function which produces output data. Each phase of the processing callback can rely on one or more algorithms in order to reuse code.
It should be noted that the box algorithm has a restricted access to kernel functionalities, and can not directly communicate with other box algorithms. A kernel accessor is provided at runtime whenever a callback is triggered, allowing for better flexibility in programming bridges to kernel objects and to other box algorithms.
Currently, the player does not take advantage of computation distribution. However, the box algorithm concept makes such distribution possible and easier because the communication is done thanks to input and output connectors and because box algorithms do not share information directly.
The scenario loading and saving plug-ins allow to keep and reuse created and configured scenarios in files. In addition to loading and saving scenarios, this plug-in can also be used to import scenarios from closely related softwares or to export scenarios to such softwares. The scenario saver basically writes each scenario component into a file. The scenario loader works the opposite way: it reads from a file and creates each box in turn, configuring and connecting boxes together as needed.