178 lines
6.3 KiB
ReStructuredText
178 lines
6.3 KiB
ReStructuredText
#############################
|
|
Contributing to lib_audio_dsp
|
|
#############################
|
|
|
|
This guide will focus on adding a DSP module into the library.
|
|
|
|
Module category
|
|
***************
|
|
|
|
This library tends to categorise modules into groups.
|
|
Have a quick look at the headers in ``lib_audio_dsp/api/dsp/`` to see if your module falls into any of those.
|
|
The headers roughly translate to the categories as they tend to group several APIs within them.
|
|
That header name would be roughly consistent in the documentation and the tests.
|
|
If you feel like your module doesn't fall into anything that's been done in this repo,
|
|
you can add a new header file and a documentation page for it.
|
|
|
|
C/asm API
|
|
*********
|
|
|
|
The library backend is only supported in two languages: C and assembly.
|
|
The supported file formats for those are: ``.c``, ``.s``, ``.S`` and ``.h`` for the header files.
|
|
If you've added an API or a typedef that's intended to be public,
|
|
it should be declared in the one of the ``lib_audio_dsp/api/dsp/`` header files and have Doxygen-style comments like:
|
|
|
|
.. code-block:: C
|
|
|
|
/**
|
|
* @brief Struct brief
|
|
*/
|
|
typedef struct{
|
|
/** Param1 description */
|
|
int32_t param1;
|
|
/** Param2 description */
|
|
int32_t param2;
|
|
}your_type_t;
|
|
|
|
/**
|
|
* @brief API brief
|
|
* Any additional description
|
|
*
|
|
* @param module Input1 parameter description
|
|
* @param in Input2 parameter description
|
|
* @return int32_t Output type and description
|
|
* @note Any notes if necessary
|
|
*/
|
|
int32_t adsp_your_api(your_type_t * module, int32_t in);
|
|
|
|
Python API
|
|
**********
|
|
|
|
Python reference API is encouraged but not necessary.
|
|
Due to the nature of modern DSP algorithm development,
|
|
we tend to prototype the new modules in python before translating and optimising them.
|
|
Python reference is also often used as an extra layer of documentation providing an easy-to-look-at view of the
|
|
algorithm without going into low-level fixed point C/assembly code.
|
|
Another use of Python reference is unit testing the backend implementation.
|
|
This allows us to see the accuracy difference between double floting point and 32-bit fixed point implementations.
|
|
|
|
If you decide to implement python reference API it should live in the appropriate file in ``python/audio_dsp/dsp``.
|
|
Your python module is expected:
|
|
|
|
- to be a class which is based on the ``dsp_block`` class
|
|
- to implement at least ``__init__``, ``process`` and ``reset_state`` methods
|
|
- to have numpydoc-style docstrings for the class and the every method of it
|
|
|
|
.. code-block:: python
|
|
|
|
import audio_dsp.dsp.generic as dspg
|
|
|
|
class your_module(dspg.dsp_block):
|
|
"""
|
|
Module description
|
|
|
|
Parameters
|
|
----------
|
|
param1 : float
|
|
Input parameter description
|
|
param2 : float
|
|
Input parameter description
|
|
|
|
Attributes
|
|
----------
|
|
param1 : float
|
|
param3 : float
|
|
Attribute description
|
|
|
|
"""
|
|
def __init__(
|
|
self, fs: float, n_chans: int, param1: float, param2: float, Q_sig: int = dspg.Q_SIG
|
|
) -> None:
|
|
super().__init__(fs, n_chans, Q_sig)
|
|
self.param1 = param1
|
|
self.param3 = param2 + param1
|
|
|
|
def reset_state(self): -> None:
|
|
"""Reset module"""
|
|
self.param1 = 0
|
|
self.param3 = 0
|
|
|
|
def process(self, sample: float, channel = 0) -> float:
|
|
"""
|
|
Process description
|
|
|
|
Parameters
|
|
----------
|
|
sample : float
|
|
The input sample to be processed.
|
|
channel : int, optional
|
|
The channel index to process the sample on. Default is 0.
|
|
|
|
Returns
|
|
-------
|
|
float
|
|
The processed sample.
|
|
"""
|
|
return sample
|
|
|
|
Optionally, you can also implement ``process_xcore`` method.
|
|
``process_xcore`` tries to provide the closest implementation to the C/assembly.
|
|
Being implemented as a 32-bit fixed point version of ``process``,
|
|
``process_xcore`` is easily testable againts the backend implementation
|
|
and should have little to no accuracy difference.
|
|
``process_xcore`` can then be used to run the module without the need of the hardware.
|
|
|
|
This library uses ``ruff`` and ``pyright`` as python formatting tools.
|
|
Both of them come as pip-installable packages and are defined in the ``requirements.txt`` file.
|
|
To make sure your python code formatting passes our CI, do:
|
|
|
|
.. code-block:: console
|
|
|
|
cd python
|
|
make check
|
|
make update
|
|
|
|
Documentation
|
|
*************
|
|
|
|
For the module documentation, choose an appropriate file in ``doc/05_api_reference/modules/``,
|
|
create a new heading/subheading with a link above it.
|
|
Put your documentation underneath the heading.
|
|
|
|
If you have a Python API as well as the C API you will have to use tabs with rubrics to refer those.
|
|
For the example, go to any ``.rst`` in ``doc/05_api_reference/modules/``.
|
|
Use ``doxygenstruct`` and ``doxygenfunction`` for the C API and structs and
|
|
``autoclass`` for python in the same way as in the rest of the documentation.
|
|
|
|
After your module is documented and the API is referenced it's time to add it to the components list!
|
|
To do that you need to go to ``doc/03_dsp_components/modules.rst``
|
|
and add a reference with the link to your heading.
|
|
|
|
Testing
|
|
*******
|
|
|
|
The backend C/assembly implementation has to be unit tested.
|
|
We accept two ways of doing that:
|
|
|
|
#. Testing against Python ``process`` or ``process_xcore``
|
|
#. Testing against the reference C implementation
|
|
|
|
In the second case the reference - an easy-to-look-at C API has to be implemented in the test source code.
|
|
|
|
For both cases, we expect to run (``xsim``) representative signals through the implementation and the chosen reference.
|
|
Your test should consider some egde cases as well as common representative use cases of the module.
|
|
|
|
Running tests should be done via running ``pytest -n auto``, so basic ``pytest`` structure should be built up first
|
|
(see how to wrap ``xsim`` into ``pytest`` in our current tests).
|
|
The tests have to be parallelisable, so if you intent to read and write files during your test,
|
|
you should consider using unique names for the test folders and/or file locks
|
|
(a lot of our tests already do that, so don't hesitate to take them as the example;).
|
|
|
|
Continuous Integration
|
|
**********************
|
|
|
|
Every module and test has to be added to our CI and pass before we can approve your pull request.
|
|
You are not expected to know the details or syntax of our CI system (Jenkins/Groovy).
|
|
After you raise your pull request, we will provide you with guidance on how to add your tests to Jenkins
|
|
and help with fixing it if it's a test/infrastructiure related issue.
|