# Contributing

In general, libraries are organised in a *stacked manner*: the base ones define functions or constants without any dependancies, and additional ones are gradually built on top of simpler ones, layer by layer. **Dependency loops must be avoided as much as possible**. The *resources* folder contains tools to build and visualise the libraries dependencies graphs.

If you wish to add a function to any of these libraries or if you plan to add a new library, make sure that you observe the following conventions:

## New Functions

* All functions must be preceded by a markdown documentation header respecting the following format (open the source code of any of the libraries for an example):

```
//-----------------`(pr).functionName`--------------------
// Description
//
// #### Usage
//
// ```
// Usage example
// ```
//
// Where:
//
// * `param1`: parameter 1 description
// * `param2`: parameter 2 description
//
// #### Example
//
// ```
// Additional example
// ```
//
// #### Test
// ```
// functionName_test = some_dsp_code;
// ```
//
// #### References
//
// * <https://some_url1>
// * <https://some_url2>
//-------------------------------------------------
```

* The `functionName` must be prefixed by the `libraryName` name prefix, like `(pr)` in the example.
* The environment system (e.g. `os.osc`) should be used when calling a function declared in another library (see the section on [Library Import](#library-import)).
* Try to reuse existing functions as much as possible.
* The `Usage` line must show the *input/output shape* (the number of inputs and outputs) of the function, like `gen: _` for a mono generator, `_ : filter : _` for a mono effect, etc. The `Where:` section then allows each parameter to be described individually, using the appropriate surrounding quotes.
* The `Example` line can be used to provide additional examples.
* The `Test` line should used to add a DSP program to test the function. The test name must be `functionName_test`. The actual code can be extracted and independantly tested using the `-pn` compiler option (to specify the name of the dsp entry-point instead of process). The test code must import all the needed libraries, like `an = library("analyzers.lib");` if a function from analyzers.lib is used in the test code. The `functionName_test` test should be added in the relevant file in the *tests* folder.
* The `References` line can be used to add links to references.
* Some functions use parameters that are [constant numerical expressions](https://faustdoc.grame.fr/manual/syntax/#constant-numerical-expressions). The convention is to label them in *capital letters* and document them preferably to be *constant numerical expressions* (or *known at compile time* in existing libraries).
* Functions with several parameters should better be written by putting the *more constant parameters* (like control, setup...) at the beginning of the parameter list, and *audio signals to be processed* at the end. This allows to do partial-application. So prefer the following  `clip(low, high, x) = min(max(x, low), high);` form where `clip(-1, 1)` partially applied version can be used later on in different contexts, better than `clip(x, low, high) = min(max(x, low), high);` version.
* Every time a new function is added, the documentation should be updated simply by running `make doclib`. <!-- TODO -->

### Layering UI-ready variants

Many functions benefit from two public faces so the same DSP can serve both low-level reuse and ready-to-tweak usage:

- *Core function*: exposes all parameters, no UI or side effects; best for reuse, composition, and testing.
- *UI wrapper*: fixes sensible defaults and exposes only runtime-tuned parameters as UI controls; leaves signals that must be provided externally as arguments.

Use the UI-free core for correctness and performance work; build the UI variant when you need something directly tweakable in examples or end-user contexts.

A generic core/UI pair could be:

```
// Core: parameters explicit, no UI
coreEffect(paramA, paramB, mix) =
  fooProcessing(mix, wet)
with {
  wet = *(paramA) : barProcessing(paramB); // barProcessing is your DSP
};

// UI wrapper: binds smoothed controls to the core
coreEffect_ui =
  coreEffect(paramA_ui, paramB_ui, mix_ui)
with {
  paramA_ui = hslider("Param A", 1.0, 0.0, 2.0, 0.01) : si.smoo;
  paramB_ui = hslider("Param B", 0.5, 0.0, 1.0, 0.01) : si.smoo;
  mix_ui    = hslider("Mix", 1.0, 0.0, 1.0, 0.01) : si.smoo;
};

process = coreEffect_ui; 
```

This keeps the core reusable (no UI dependencies) and the wrapper ready for immediate tweaking; `process` points to the UI layer for quick testing.

#### Instrument-specific three-layer pattern

Instrument models often add a third, ready-to-play layer. The clarinet model is a reference:

- `pm.clarinetModel(tubeLength, pressure, reedStiffness, bellOpening)`: core DSP with every parameter explicit and no UI.
- `pm.clarinetModel_ui(pressure)`: wraps the core and adds UI sliders for tube length, reed stiffness, bell opening, and output gain; keeps `pressure` as an argument.
- `pm.clarinet_ui_MIDI`: builds a playable instrument by pairing the core with a blower/envelope plus MIDI-mapped UI (pitch bend, sustain, vibrato, gain, etc.).

When adding similar models, start with the UI-free core, add a minimal UI wrapper, then optionally provide a controller-specific wrapper (MIDI or otherwise). Keep the core independent so it remains reusable.


### Variables and identifiers scoping

To avoid name clashes between libraries, keep identifiers as local as possible. Prefer defining intermediate constants and helpers inside `with { ... }` blocks or `environment { ... }` sections, and only expose the intended public entry points. This minimizes collisions when several libraries are imported together and keeps global namespace usage limited to documented, public-facing functions.

## New Libraries

* Any new "standard" library should be declared in `stdfaust.lib` with its own environment (2 letters - see `stdfaust.lib`) and in `all.lib`.
* Some of the new library functions should be demonstrated in [demos.lib](#demo-functions).
* Any new "standard" library must be added to `generateDoc`.
* Functions must be organized by sections.
* Any new library should at least `declare` a `name` and a `version`.
* Any new library has to use a prefix declared in the header section with the following kind of syntax: `Its official prefix is 'qu'` (look at an existing library to follow the exact syntax).
* Be sure to add the appropriate kind of `ma = library("maths.lib");` import library line, for each external library function used in the new library (for instance `ma.foo` that would be used somewhre in the code).
* The comment based markdown documentation of each library must respect the following format (open the source code of any of the libraries for an example):

```
//############### libraryName ##################
// Description
//
// * Section Name 1
// * Section Name 2
// * ...
//
// It should be used using the `[...]` environment:
//
// ```
// [...] = library("libraryName");
// process = [...].functionCall;
// ```
//
// Another option is to import `stdfaust.lib` which already contains the `[...]`
// environment:
//
// ```
// import("stdfaust.lib");
// process = [...].functionCall;
// ```
//##############################################

//================= Section Name ===============
// Description
//==============================================
```

## Coding Conventions

In order to have a uniformized library system, we established the following conventions (that hopefully will be followed by others when making modifications to them).

### Function Naming

[WIP]

JOS proposal: using terms used in the field of digital signal processing, as follows:

* `impulse`: ...,0,1,0,...
* `pulse`: ...,0,1,1,0,... or longer
* `impulse_train`
* `pulse_train`
* `gate` = pulse controlled externally (e.g., by NoteOn,NoteOff)
* `trigger` = impulse controlled externally (gate - gate' > 0) == gate rising edge

[/WIP]

### Variable Argument List 

 Strictly speaking, there are no lists in Faust. But list operations [can be simulated](https://faustdoc.grame.fr/manual/faq/#pattern-matching-and-lists) (in part) using the parallel binary composition operation `,` and pattern matching.

Thus functions expecting a variable number of arguments can use this mechanism, like a `foo` function that would be used this way: `foo((a,b,c,d))`. See [fi.iir](https://faustlibraries.grame.fr/libs/filters/#fiiir) and [fi.fir](https://faustlibraries.grame.fr/libs/filters/#fifir) examples.

### Documentation

* All the functions that we want to be "public" are documented.
* We used the `faust2md` "standards" for each library: `//###` for main title (library name - equivalent to `#` in markdown), `//===` for section declarations (equivalent to `##` in markdown) and `//---` for function declarations (equivalent to `####` in markdown - see `basics.lib` for an example).
* Sections in function documentation should be declared as `####` markdown title.
* Each function documentation provides a "Usage" section (see `basics.lib`). 
* The full documentation can be generated using the doc/Makefile script. Use `make help` to see all possible commands. If you plan to create a pull-request, *do not commit the full generated code* but only the modified .lib files.
* Each function can have `declare author "name";`, `declare copyright "XXX";` and `declare licence "YYY";` declarations.
* Each library has a `declare version "xx.yy.zz";` [semantic version](https://semver.org) number to be raised each time a modification is done. The global `version` number in `version.lib` also has to be adapted according to the change.  

### Library Import

To prevent cross-references between libraries, we generalized the use of the `library("")` system for function calls in all the libraries. This means that everytime a function declared in another library is called, the environment corresponding to this library needs to be called too. To make things easier, a `stdfaust.lib` library was created and is imported by all the libraries:

```
aa = library("aanl.lib");
sf = library("all.lib");
an = library("analyzers.lib");
ba = library("basics.lib");
co = library("compressors.lib");
db = library("debug.lib");
de = library("delays.lib");
dm = library("demos.lib");
dx = library("dx7.lib");
en = library("envelopes.lib");
fd = library("fds.lib");
fi = library("filters.lib");
ho = library("hoa.lib");
hy = library("hysteresis.lib");
it = library("interpolators.lib");
la = library("linearalgebra.lib");
ma = library("maths.lib");
mi = library("mi.lib");
ef = library("misceffects.lib");
mo = library("motion.lib");
no = library("noises.lib");
os = library("oscillators.lib");
pf = library("phaflangers.lib");
pl = library("platform.lib");
pm = library("physmodels.lib");
qu = library("quantizers.lib");
rm = library("reducemaps.lib");
re = library("reverbs.lib");
ro = library("routes.lib");
sp = library("spats.lib");
si = library("signals.lib");
so = library("soundfiles.lib");
sy = library("synths.lib");
ve = library("vaeffects.lib");
vl = library("version.lib");
wa = library("webaudio.lib");
wd = library("wdmodels.lib");
```

For example, if we wanted to use the `smooth` function which is now declared in `signals.lib`, we would do the following:

```
import("stdfaust.lib");

process = si.smooth(0.999);
```

This standard is only used within the libraries: nothing prevents coders to still import `signals.lib` directly and call `smooth` without `ro.`, etc. It means symbols and function names defined within a library **have to be unique to not collide with symbols of any other libraries**.  

### "Demo" Functions

"Demo" functions are placed in `demos.lib` and have a built-in user interface (UI). Their name ends with the `_demo` suffix. Each of these function have a `.dsp` file associated to them in the [Faust project](https://github.com/grame-cncm/faust) `examples` folder.

Any function containing UI elements should be placed in this library and respect these standards.

### "Standard" Functions

"Standard" functions are here to simplify the life of new (or not so new) Faust coders. They are declared in `/libraries/doc/standardFunctions.md` and allow to point programmers to preferred functions to carry out a specific task. For example, there are many different types of lowpass filters declared in `filters.lib` and only one of them is considered to be standard, etc.

## Testing the library

Before preparing a pull-request, the new library must be carefully tested:

- all functions defined in the library must be tested by preparing a DSP test program, to be added using the `#### Test` syntax 
- the compatibility library `all.lib` imports all libraries in a same namespace, so check functions names collisions using the following test program: `import("all.lib"); process = _;`
- reference files for all tests can be generated using the `make reference` command and then verified with the `make check` command, which compares the generated samples against the reference files within a specified tolerance. A good practice for developers is therefore to generate the reference files and re-run the checks whenever the code is modified.

## LLMs

The site exposes an `llms.txt` file generated from `doc/docs/llms.txt` and published at [https://faustlibraries.grame.fr/llms.txt](https://faustlibraries.grame.fr/llms.txt).

### Faust Library JSON Exports

This repository can also generate machine-readable JSON exports of the Faust
library documentation directly from the `.lib` sources.

The generator is:

```bash
scripts/build_faust_doc_index.py
```

It parses documentation blocks from the library sources, starting from
`stdfaust.lib`, follows `library("...")` and `import("...")` directives, and
extracts for each documented symbol:

- `summary`
- `usage`
- `params`
- `notes`
- `io` with `inSignals` / `outSignals` when derivable
- `testCode`
- `references`
- `license` when a per-symbol `declare ... license|licence "..."` is present
- `source`

Two JSON layouts are supported:

- monolithic: one full JSON file containing all libraries and symbols
- split: one compact global index plus one detailed JSON file per module

Default Make targets:

```bash
make doc-index
make doc-index-split
make doc-index-commercial
```

Default output locations:

- `make doc-index` writes `tests/faust-doc-index.json`
- `make doc-index-split` writes:
  - `tests/faust-doc-index.json`
  - `tests/faust-doc/index.json`
  - `tests/faust-doc/modules/*.json`
- `make doc-index-commercial` writes the same paths as `make doc-index-split`,
  but filters the exported symbols using the `commercial-compatible` license
  policy

You can override the output paths:

```bash
make doc-index DOC_INDEX_OUTPUT=/tmp/faust-doc-index.json
make doc-index-split DOC_INDEX_OUTPUT=/tmp/faust-doc-index.json DOC_INDEX_SPLIT_DIR=/tmp/faust-doc
```

You can also run the generator directly:

```bash
python3 scripts/build_faust_doc_index.py --repo-root . --output tests/faust-doc-index.json --pretty
python3 scripts/build_faust_doc_index.py --repo-root . --output tests/faust-doc-index.json --split-output-dir tests/faust-doc --pretty
python3 scripts/build_faust_doc_index.py --repo-root . --output tests/faust-doc-index.json --split-output-dir tests/faust-doc --license-policy commercial-compatible --pretty
```

License-policy filtering is optional. The supported values are:

- `all`: export every documented symbol
- `commercial-compatible`: keep only symbols that pass a conservative
  per-symbol license heuristic

The current `commercial-compatible` heuristic:

- accepts missing per-symbol licenses and treats them as falling back to the
  library default
- accepts common permissive or weak-copyleft markers such as `MIT`, `BSD`,
  `Apache`, `LGPL`, `LGPL with exception`, `MPL`, `ISC`, `zlib`, `Boost`,
  `Unlicense`, `public domain`, and `STK-4.3`
- rejects markers such as `GPL`, `AGPL`, and explicitly non-commercial terms

This is a practical export filter for tooling, not a legal opinion.

The policy can also be customized with external allowlist/denylist files:

```bash
python3 scripts/build_faust_doc_index.py \
  --repo-root . \
  --output tests/faust-doc-index.json \
  --split-output-dir tests/faust-doc \
  --license-policy commercial-compatible \
  --license-allowlist-file /path/to/license-allowlist.txt \
  --license-denylist-file /path/to/license-denylist.txt \
  --pretty
```

These files use a simple newline-based format:

- one token or pattern per line
- matching is case-insensitive and based on substring inclusion
- empty lines are ignored
- lines starting with `#` are treated as comments

Example:

```text
# Allow permissive licenses and a specific local marker
mit
bsd
apache
my-company-approved-license
```

The Make target also supports these overrides:

```bash
make doc-index-commercial \
  DOC_INDEX_LICENSE_ALLOWLIST_FILE=/path/to/license-allowlist.txt \
  DOC_INDEX_LICENSE_DENYLIST_FILE=/path/to/license-denylist.txt
```

The split layout is recommended for LLM or retrieval-based use because it avoids
loading the whole documentation into context for every request.

### Local Documentation Query API

The repository also ships with a local query tool that reproduces the Faust
library documentation operations used in `faustforge`:

```bash
scripts/faust_doc_api.py
```

Supported operations:

- `search_faust_lib`
- `get_faust_symbol`
- `list_faust_module`
- `get_faust_examples`
- `explain_faust_symbol_for_goal`

The tool can read either:

- the monolithic export `tests/faust-doc-index.json`
- the split export `tests/faust-doc/index.json`

If `--index` is omitted, it tries these defaults in that order:

1. `tests/faust-doc/index.json`
2. `tests/faust-doc-index.json`

Examples:

```bash
python3 scripts/faust_doc_api.py --pretty search_faust_lib reverb --limit 5
python3 scripts/faust_doc_api.py --pretty search_faust_lib filter --module filters --limit 10

python3 scripts/faust_doc_api.py --pretty get_faust_symbol de.delay
python3 scripts/faust_doc_api.py --pretty list_faust_module delays --limit 20
python3 scripts/faust_doc_api.py --pretty get_faust_examples delay
python3 scripts/faust_doc_api.py --pretty explain_faust_symbol_for_goal re.springreverb "build a metallic spring reverb"
```

To force a specific index location:

```bash
python3 scripts/faust_doc_api.py --index tests/faust-doc-index.json --pretty get_faust_symbol aa.Rsqrt
python3 scripts/faust_doc_api.py --index tests/faust-doc --pretty list_faust_module filters --limit 10
```

The `make clean` target removes the generated JSON artifacts by default:

- `tests/faust-doc-index.json`
- `tests/faust-doc/`

### Licensing issue

Some DSP functions carry per-symbol licenses that are more restrictive than the
default Faust libraries distribution terms. This matters in LLM-assisted code
generation and code reuse workflows.

In particular, functions released under licenses that are non-commercial,
copyleft-incompatible, or otherwise unsuitable for the intended downstream use
**must not be suggested blindly in generated code**.

When exposing Faust library data to LLM tools or retrieval systems:

- preserve per-function license metadata when it exists
- make that metadata queryable alongside the usual documentation fields
- check license compatibility before recommending or assembling generated DSP
  code from library functions

The local JSON export and query tooling can expose this information, so **license
checks can be integrated into higher-level assistants and generation pipelines**.


## Library test and deployment

For GRAME maintainers:

- global tests can be done using the `make reference` and `make check` at rool level
- regenerate the PDF documentation using `make pdf` target at rool level
- update the library submodule in [faust](https://github.com/grame-cncm/faust), recompile and deploy WebAssembly libfaust in [fausteditor](https://github.com/grame-cncm/fausteditor), [faustplayground](https://github.com/grame-cncm/faustplayground) and [faustide](https://github.com/grame-cncm/faustide)
- update the library submodule in [faustlive](https://github.com/grame-cncm/faustlive) 
- update the library list in this [fausteditor](https://github.com/grame-cncm/fausteditor/blob/master/src/faustlive.js) page as well as the [snippets](https://github.com/grame-cncm/fausteditor/blob/master/src/codemirror/mode/faust/faustsnippets.js) (using the `faust2atomsnippets -s *.lib` command at root level).
- update the library list in this [faustide](https://github.com/grame-cncm/faustide/blob/master/src/documentation.ts) page and [LIBRARIES](https://github.com/grame-cncm/faustide/tree/master/src/static/examples/LIBRARIES) folder.
- update the library list in the [faustgen~](https://github.com/grame-cncm/faust/blob/master-dev/embedded/faustgen/src/faustgen_factory.cpp) code
- update the [Faust Syntax Highlighting Files](https://github.com/grame-cncm/faust/tree/master-dev/syntax-highlighting)
- make an update PR for [vscode-faust](https://github.com/hellbent/vscode-faust) project
