Sygaldry
Loading...
Searching...
No Matches
Making a New Component

Copyright 2023 Travis J. West, Input Devices and Music Interaction Laboratory (IDMIL), Centre for Interdisciplinary Research in Music Media and Technology (CIRMMT), McGill University, Montréal, Canada, and Univ. Lille, Inria, CNRS, Centrale Lille, UMR 9189 CRIStAL, F-59000 Lille, France

SPDX-License-Identifier: MIT

The features and functionality provided by the Sygaldry library are almost all implemented as reusable software components. Many new designs will require new components to be implemented, e.g. to interface with sensors that aren't already supported by the library, to add new binding protocols, and so on.

The skeleton of a new component can be automatically generated with sh/new_component.sh New Component Convenience Script. Run the script in the build environment to get started. After generating the skeleton, continue with the rest of this document for more information on the rationale behind the design of a Sygaldry component.

Logical Anatomy of a Component

Logically (i.e. in terms of its logical software design, as opposed to the physical layout of files described below) a component is simply a structure with metadata, endpoints, and at least a main subroutine. The init and main subroutines of the component will be called by the sygaldry::Runtime as described in Making a New Instrument.

To make it easier to use other components and helpers from within a new component, it should be placed within the top level sygaldry namespace. To encapsulate the new component and avoid name collisions, it should be further placed in an appropriate package namespace depending on what the component implements and the platform-portability of the implementation. We will use the example namespace sygXX

namespace sygaldry { namespace sygXX {
struct NewComponent
: // metadata helpers here
{
struct inputs_t {
// input endpoints here
} inputs;
struct outputs_t {
// output endpoints here
} outputs;
void init()
{
// initialization subroutine, optional
}
void main()
{
// main subroutine, required
}
};
} }

Metadata

Metadata is attached to a component (and for that matter to an endpoint) in one of a few ways. In particular, all components must have a static consteval auto name() method that returns a string. The name is expected to be title case with spaces. An OSC address for the component is automatically generated by replacing the spaces with underscores. It is recommended to inherit from the sygaldry::name_ helper, as well as the related helpers described in sygah-metadata: Metadata Helpers, to add other useful information such as the author and copyright of the component, as seen in the following example:

#include "sygah-metadata.hpp"
namespace sygaldry { namespace sygXX {
struct NewComponent
: name_<"COMPONENT NAME HERE">
, description_<"COMPONENT DESCRIPTION HERE">
, author_<"COMPONENT AUTHOR HERE">
, copyright_<"Copyright 2023 Sygaldry Contributors">
, license_<"SPDX-License-Identifier: MIT">
, version_<"0.0.0">
{
// ...
};
} }

The metadata helpers are defined in the sygaldry namespace, so by including our component in this namespace, we don't have to qualify the helpers names with sygaldry:: every time we use them.

Endpoints

The inputs and outputs members of the component should be simple aggregate structures. Other than this constraint, these members may contain any data needed by the component. The inputs structure is not allowed to be modified in the main subroutine. The init subroutine should set both structures to a known initial state.

If an endpoint is self-documented appropriately, then binding components will be able to automatically expose the endpoint, e.g. to Open Sound Control or persistent session storage. A variety of helpers are provided to make this easy; it is recommended to review sygah-endpoints: Endpoints Helpers for more information.

For example, here is one way to declare a floating point input called "gain" that goes from 0 to 10 and should be stored persistently across power cycles, an input using the default range that is only expected to be updated sometimes called "in", and an output array of 30 floats that is only updated when "in" changes:

struct inputs_t {
slider<"gain"
, "amount of gain applied to the input"
, float, 0.0f¸ 10.0f, 0.0f
, tag_session_data
> gain;
slider_message<"in"
, "input to the unspecified example algorithm"
> in;
} inputs;
struct outputs_t {
array_message<"out", 30, "only updated when `in` changes",
, float
> out;
} outputs;

Like the metadata helpers, endpoint helpers are part of the sygaldry namespace, so their names don't have to be fully qualified if our component is defined in that namespace.

Subroutines

Several subroutines are expected by the runtime. Notably, the init subroutine may modify both the inputs and outputs of the component and should set them in an initial state so that the component is ready to run, and the main subroutine should implement the principal functionality of the component and update the outputs accordingly. The main subroutine should not modify the inputs of the component.

Physical Anatomy of a Component

Physically, Sygaldry components are implemented as software components with a uniform layout. All components have a unique identifier of the form sygXX-component_name, where sygXX is the identifier of a package group, such as sygsp for portable sensor components, and component_name is a descriptive name for the component.

The uniform physical layout of a component consists of the following requirements. All of these requirements are met when a new component is generated with the the new component helper script, so authors generally needn't worry about them:

  • the component is located in a directory whose name is the component ID
  • the component directory contains a literate source file *.lili.md, where * is the component ID
  • the literate source file is marked as a Doxygen page named page-*
  • that page is added as a subpage to the literate source code index
  • the literate source file generates a CMakeLists.txt file that declares a library whose name is the component ID
  • the component directory is added to the build system with a call to add_subdirectory, and the library is linked to the component's package group with target_link_libraries.
  • the component should have a single header file *.hpp where * is the component ID
  • the component may have a single implementation file *.cpp.

The literate source code index page is automatically generated by one script, and the main CMakeLists.txt that adds and links libraries is automatically generated by another. Authors are therefore only required to ensure that the literate source exists, is marked as a page, and generates an appropriate CMakeLists.txt for the component. Of these requirements, only the CMakeLists.txt will generate a show-stopping error if it is not met, as CMake will be unable to build anything unless it can find a library with the component ID for its name.

The uniform naming requirements for component directories, Doxygen pages, header file, and CMake library targets, taken together, facilitate scripting, refactoring, and readability. You can always tell by inspection when one component depends on another, it's easy to tell the origin of symbols in the source code, and changing the name of a component can be achieved with simple find-and-replace scripts, for example.

Literate Anatomy of a Component

Literate programming is a fundamental part of the Sygaldry project. Every component should be implemented in a literate source file using lili annotations to allow the machine code to be extracted, include doxygen documentation, and have a copyright and license statement.

For more information on using lili, see Literate Programming with lili.

For more information on using doxygen, see Literate Programming with Doxygen.

Copyright and License Text

Finally, all source files (both literate and machine) must have a copyright statement and license identifier at the top of the file, as seen in all documents in the repository. Contributors are welcome to use whatever license they feel is appropriate, although MIT is encouraged for consistency when allowed.