Plug-ins are used to extend the functionality of Speect, and as a mechanism to keep the Speect Engine independent of any linguistic or acoustic specific code. This extending of Speect is done by defining new classes (see Generic Object System) in the plug-ins and registering them with the object system, so that objects of these new classes can be instantiated and used by other code. See plug-ins C API for a detailed description of the API.
In Speect each plug-in is in it’s own directory. A template of this plug-in directory format is provided in speect/plugins/template:
speect/plugins/template/
├── AUTHORS
├── cmake
│ └── sources.cmake
├── CMakeLists.txt
├── README
└── src
├── plugin.c
├── source.c
└── source.h
Below follows a discussion of defining a plug-in with regards to this template.
Plug-ins are deployed as dynamic libraries. Dynamic libraries allow many of the advantages of plug-ins such as hot swapping (reloading a new implementation without shutting the system), safe extension by third-party developers (additional functionality without modifying the core system), and shorter link times.
Speect provides support for the use of dynamic shared objects (DSO) as plug-ins. When a DSO, or plug-in, is loaded the plug-in manager looks for the s_plugin_init symbol in the plug-in. This is the only symbol that is loaded by the plug-in manager and therefore must be defined by all plug-ins that want to work with the plug-in manager.
The template provides a simple function that defines the s_plugin_init symbol, this function is used unchanged by all implemented plug-ins, as the defined symbols are created by the build system (see plugin_info.h):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | S_PLUGIN_API const s_plugin_params *s_plugin_init(s_erc *error)
{
S_CLR_ERR(error);
if (!s_lib_version_ok(SPCT_MAJOR_VERSION_MIN, SPCT_MINOR_VERSION_MIN))
{
S_CTX_ERR(error, S_FAILURE,
SPCT_PLUGIN_INIT_STR,
"Incorrect Speect Engine version, require at least '%d.%d.x'",
SPCT_MAJOR_VERSION_MIN, SPCT_MINOR_VERSION_MIN);
return NULL;
}
return &plugin_params;
}
|
This function is called by the plug-in manager. It basically does a version check and then returns the s_plugin_params structure for the specific plug-in to the plug-in manager. The template also defines this structure, and it can be used unchanged in any plug-in as the defined symbols are created by the build system (see plugin_info.h):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | static const s_plugin_params plugin_params =
{
/* plug-in name */
SPCT_PLUGIN_NAME,
/* description */
SPCT_PLUGIN_DESCRIPTION,
/* version */
{
SPCT_PLUGIN_VERSION_MAJOR,
SPCT_PLUGIN_VERSION_MINOR
},
/* Speect ABI version (which plug-in was compiled with) */
{
S_MAJOR_VERSION,
S_MINOR_VERSION
},
/* register function pointer */
plugin_register_function,
/* exit function pointer */
plugin_exit_function
};
|
The two important members in this structure are on lines 22 and 25. These are the register and exit functions (with signatures as defined in s_plugin_reg_fp and s_plugin_exit_fp) that the plug-in manager calls on registering the plug-in and freeing it. In these two functions one must register and free the plug-in’s provided classes with the Speect object system (s_class_reg() and s_class_free()), see for example the shape class registering and freeing functions. The template provides a skeleton implementation of these two functions which should be completed with the correct class registration and freeing for your plug-in:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /* plug-in register function */
static void plugin_register_function(s_erc *error)
{
S_CLR_ERR(error);
/* TODO: your plug-in register function */
/* register plug-in classes here with s_class_reg */
S_CHK_ERR(error, S_CONTERR,
SPCT_PLUGIN_REG_STR,
SPCT_PLUGIN_REG_FAIL_STR);
}
/* plug-in exit function */
static void plugin_exit_function(s_erc *error)
{
S_CLR_ERR(error);
/* TODO: your plug-in free function here */
/* free plug-in classes here with s_class_free */
S_CHK_ERR(error, S_CONTERR,
SPCT_PLUGIN_EXIT_STR,
SPCT_PLUGIN_EXIT_FAIL_STR);
}
|
The template provides skeleton source and header files which can be used to implement a plug-in. Each implemented class has registration and freeing functions in the following format:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | S_LOCAL void _s_your_class_reg(s_erc *error)
{
S_CLR_ERR(error);
s_class_reg(S_OBJECTCLASS(&YourClass), error);
S_CHK_ERR(error, S_CONTERR,
"_s_your_class_reg",
"Failed to register SYourClass");
}
S_LOCAL void _s_your_class_free(s_erc *error)
{
S_CLR_ERR(error);
s_class_free(S_OBJECTCLASS(&YourClass), error);
S_CHK_ERR(error, S_CONTERR,
"_s_your_class_free",
"Failed to free SYourClass");
}
|
These functions are used in the plug-in registration and exit functions.
Lets say we implemented the shape, circle and rectangle classes as discussed in the Generic Object System example. Now the order of registering and freeing classes in the plugin_register_function and plugin_exit_function becomes very important because of the way the object system class registration functions work.
When a class is registered with s_class_reg(), the object system will create a structure containing the class’s inheritance hierarchy of initialization and destroy functions. The circle class inherits from the shape class, and therefore must be registered after the shape class, otherwise the object system cannot find the unregistered shape class’s init and destroy functions.
This restriction on the order of registering classes does not hold when freeing classes in the ``plugin_exit_function with the s_class_free() function.
Lets say we implemented the shape, circle and rectangle classes as plug-ins, each in it’s own directory. The circle class inherits from the shape class and therefore requires it’s plug-in. This can be done by first loading the shape plug-in in the plugin_register_function of the circle plug-in. We create a static plug-in variable to hold the shape plug-in and then load the plug-in.
static SPlugin *shapePlugin = NULL;
static void plugin_register_function(s_erc *error)
{
S_CLR_ERR(error);
shapePlugin = s_pm_load_plugin("shape.spi", error);
...
s_pm_load_plugin() will load the DSO named “shape.spi” (Speect uses the ”.spi” extension for it’s plug-ins), look up the s_plugin_init symbol which includes the shape class’s registering function, execute it, and set the DSO and related information in the SPlugin object.
Note
If the given plug-in path does not include any path separators (just a file name) then the path is concatenated with the default plug-in path.
The same rules for of order of registration holds, in other words we must first load the shape plug-in before registering the circle class. And then in the plugin_exit_function we can delete the shape plug-in:
static void plugin_exit_function(s_erc *error)
{
S_CLR_ERR(error);
S_DELETE(shapePlugin, "plugin_exit_function", error);
...
We have to include the shape header in our source code if we want to use any shape defined members or methods. We don’t need to include the whole path to the specific location of the shape plug-in implementation as the build system will take care thereof.
The following sections describe the build system files of a plug-in, again in reference to the template plug-in.
The source files of the plug-in needs to be specified in speect/template/cmake/sources.cmake:
######## source files ##################
speect_plugin_sources(
src/plugin.c
src/source.c
)
######## header files ##################
speect_plugin_headers(
src/source.h
)
Any additional sources that make up part of the plug-in must be included here.
CMakeLists.txt is the top-level CMake specification file for the plug-in. The following describes the CMake commands executed to create a plug-in project:
Define plug-in
First a plug-in definition is required:
speect_plugin_definition(Plugin_Name "Plugin_Class_Name" 1 0 0) # version is major minor patch
The plug-in is defined by the speect_plugin_definition macro, which does the following:
- check if a plug-in with the same name, Plugin_Name, already exists (all plug-ins must have unique names),
- create a CMake project for the plug-in,
- create DSO names for the plug-in, “lowercase_name.spi” with links to - “lowercase_name.spi.version_major” and - “lowercase_name.spi.version_major.version_minor.version_patch”
- and create a class name for the plug-in which is used in the defined symbols in plugin_info.h.
For example:
speect_plugin_definition(Viterbi "SViterbi" 0 9 5)
will create a plug-in DSO named “viterbi.spi” that points “to viterbi.spi.0” which in turn points to “viterbi.spi.0.9.5”.
Note
The plug-in’s name (Plugin_Name) will be lower cased, and can be used to reference the plug-in in other plug-ins projects. See for example the utterance processors definitions in the voice file where an utterance processor plug-in is reference by it’s name.
Note
All plug-in DSO’s are place in the build directory plugins/lib (see Configuration and Compilation), which is usually speect/build/plugins, and is the default plug-in path. The plug-in manager takes care of the paths and the plug-ins can be referenced by just their names and extensions (always ”.spi”), for example see the voice feature processors definition in the voice topic.
Configure plugin_info.h
All plug-ins must include the plugin_info.h header file in the plugin.c source file. This header file is generated by the build system based on information in CMakeLists.txt. The configuration is as follows:
set(description "A simple plug-in description")
# Minimum required Speect Engine version
set(major_min 1)
set(minor_min 0)
speect_plugin_configure_info(${description} ${major_min} ${minor_min})
A description, and version numbers are set, and then the speect_plugin_configure_info is called to generate plugin_info.h, which defines the following C pre-processor variables:
- SPCT_PLUGIN_NAME: the plug-in name, which is the class name created by speect_plugin_definition,
- SPCT_PLUGIN_DESCRIPTION: the description that was set,
- SPCT_PLUGIN_INIT_STR : the initialization string used in s_plugin_init,
- the SPCT_PLUGIN_REG_STR, SPCT_PLUGIN_REG_FAIL_STR, SPCT_PLUGIN_EXIT_STR and SPCT_PLUGIN_EXIT_FAIL_STR strings used in the plug-in register and exit functions,
- SPCT_PLUGIN_VERSION_MAJOR and SPCT_PLUGIN_VERSION_MINOR version variables, defined by speect_plugin_definition,
- and the minimum required Speect Engine version variables SPCT_MAJOR_VERSION_MIN and SPCT_MINOR_VERSION_MIN.
The strings variables are defined just to make the definitions of s_plugin_init and the plug-in register and exit functions less tedious when implementing a lot of plug-ins, but they can of course be ignored are replaced in those functions with something more appropriate if desired. The version variables are used in a version handshaking function between the plug-in and the Speect Engine in the plug-in manager.
Source files and includes
The sources.cmake file that defines the list of source and header files of the plug-in is added to the plug-in project:
include(sources)
Other plug-in’s projects that are used in this plug-in must also be included (their correct paths will be determined by the build system), for example:
include(shape)
This will allow us to use their headers in this plug-in. The name is the lower cased plug-in project name, the same way it is defined in speect_plugin_definition. A list of plug-in project configurations that can be included are in speect/build/plugins/cmakeconf.
Plug-in DSO
and finally we create the plug-in DSO with the speect_plugin_create function:
speect_plugin_create()
This function will create the plug-in shared object, which will be linked against the Speect Engine library. The build options (Debug, etc.) will the same as for the Speect Engine library. Optional parameters, which are libraries to link with, can be given in a list form.
The full CMakeLists.txt CMake project file is given in the examples.