Brickworks

Brickworks API

Brickworks is a music DSP toolkit that offers a solid foundation for creating and enhancing audio engines on any platform.

This documentation contains technical information on using the Brickworks API. You can find background information on what Brickworks actually is on its official web page.

Technical background

Brickworks is written in plain C99 and only requires:

The API user is given some flexibility in supplying such definitions (see bw_config and bw_common).

The code is organized in separate modules which are as independent as possible but still may require (i.e., depend on) other modules. Each module consists of an header file having filename module.h.

Brickworks is not meant to be built and used as a static or shared library. You should rather just use the needed module files and to develop your software (more below).

Both individual modules and the whole library are versioned according to the Semantic Versioning Specification 2.0.0. It follows that the library version will be equal to the version of the last modified module, while modules that were not modified will be tagged with a previous version number. Module dependencies are considered part of the API, therefore if a dependency is removed that would lead to MINOR version change, and if one is added that would cause a MAJOR version change.

Unless otherwise stated, the C API follows the following conventions:

Conventions for C++ wrappers will be added when they are released.

Getting started

To be written, topics:

Modules

Foundation

These modules are required by most, if not all, other modules and just contain a few basic essential definitions. They provide a certain degree of consistency and adaptability to the rest of the code.

You can also directly use them to create your own modules, if you want.

List

Utility

Utility modules contain heterogeneous code that is repeatedly used by other modules.

List

DSP

These modules implement signal processing blocks (not necessarily audio-related).

Their APIs are not 100% uniform, but they tend to follow a common pattern described here.

Each signal processing block conceptually consists of:

Signal inputs and outputs are not explicitly represented by any data structure, but you rather see them as arguments or return values of processing functions. Signal data is PCM-encoded as (buffers of) 32-bit floating point numbers, with values typically between -1.f and 1.f (these correspond to the 0 dBFS clipping points — lying within this range is not a strict requirement), and uniformly-sampled at a user-supplied sample rate, which is common to all signal inputs and outputs of a block instance. Signals that vary at such sample rate are also known as audio rate signals.

Parameters, on the other hand, can have different types and ranges and are asynchronously updated at a variable rate, also known as control rate, that is lower than the sample rate. You can set input parameter values and get output parameter values using specific functions. As long as input parameter values are valid and set properly, you can expect to freely change them without incurring in glitches or other artifacts.

Coefficients are calculated and kept up to date by various functions at control and/or audio rate on the basis of input parameter values. As an API user you don't directly interact with them. They are stored together with input parameter values and output parameter values that do not depend on state into a common data structure which can be either specific to one block instance or shared by more instances (in which case those instances act like they have all the same parameter settings). In the former case you can use buffer-based processing functions, which also take care of updating coefficients, otherwise you are forced to use sample-based processing functions and explicitly trigger control-rate and audio-rate coefficient updates.

As coefficients and output parameter values may be subject to smoothing or other dynamic updating patterns, their values may evolve over time even if input parameter values are constant from a certain moment on. However, they are required to converge towards fixed values given constant input parameter values and sample rate. This can be exploited by the API user to, e.g., completely avoid triggering updates when input parameter values are constant from the start, by invoking functions that force coefficients to assume their target values.

Each block instance is also uniquely characterized by a data structure containing its internal state and output parameter values that depend on state, whose contents are not normally accessible to API users. Such internal state and output parameter values are typically updated at audio rate by processing functions and can be reset by the API user to a set of initial values, which can depend on input parameter values, coefficient values, and sample rate, via specific functions. However, there are cases in which such values are also updated at control rate, which is automatically handled by buffer-based processing functions, unlike sample-based ones.

In each module you may find:

Before bw_module_init() is called all parameters, coefficients, and states are considered invalid.

Any varations to the pattern above are indicated in the individual module's documentation.

List

Glossary

Definitions in this glossary are not meant to be general but only valid in the context of using the Brickworks API.

No side effects
A function has no side effects if it does not affect any external state (except data referenced by arguments).
Reentrant function
A function that does not read external variable data (beyond input data).
RT-safe function
A function that has finite and deterministic execution time (e.g., it doesn't block or loop undefinitely), with at worst linear time complexity w.r.t. input data.
Thread-safe function
A function that is safe to use concurrently by multiple threads operating on the same input and external data.