Sygaldry
|
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.
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
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:
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.
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:
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.
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.
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:
*.lili.md
, where *
is the component IDpage-*
CMakeLists.txt
file that declares a library whose name is the component IDadd_subdirectory
, and the library is linked to the component's package group with target_link_libraries
.*.hpp
where *
is the component ID*.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 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.
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.