This commit is contained in:
Steven Dan
2025-12-11 09:43:42 +08:00
commit d8b2974133
1822 changed files with 280037 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
.. raw:: latex
\newpage
.. _tool_user_guide_section:
Tool User Guide
###############
This section introduces the `audio_dsp` Python library, and how to use it to generate multithreaded DSP pipelines for the xcore.
The following sections provide guidance on preparing the environment for the library, building a pipeline, and exploring the API documentation.
.. note::
For a quick start, the Application Notes related to lib_audio_dsp on the
`XMOS website <https://www.xmos.com/file/lib_audio_dsp#related-application-notes>`_ come
preconfigured with a complete application and sandbox.
.. toctree::
:maxdepth: 2
setup
using_the_tool

View File

@@ -0,0 +1,249 @@
.. |xtc_tools_version| replace:: 15.3.1
.. |python_version| replace:: 3.12
.. |cmake_version| replace:: 3.21
.. _CMAKE: https://cmake.org/cmake/help/latest/
.. _Python: https://www.python.org/downloads/
.. _Graphviz: https://graphviz.org/download/
Setup
#####
This section describes the requirements and the steps to run a basic pipeline.
This document lists the necessary steps for both Windows and Linux/macOS.
This section uses the *app_simple_audio_dsp_integration* example found within this repository.
The steps will be broadly similar for any user-created project.
.. note::
Copying multiple lines into the console may not work as expected on Windows.
To avoid issues, copy and execute each line individually.
Hardware Requirements
=====================
- xcore.ai evaluation board (`XK-EVK-XU316`_ or `XK-316-AUDIO-MC-AB`_)
- 2x Micro USB cable (one for power supply and one for the XTAG)
.. _sw_reqs:
Software Requirements
=====================
- `XTC tools`_: |xtc_tools_version|.
- Graphviz_: this software must be installed and the ``dot`` executable must be on the system path.
- Python_: |python_version| or later.
- CMAKE_: |cmake_version| or later.
Additionally, on Windows the following is required:
- `ninja-build <https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages#user-content-windows>`_
.. _all_steps:
Setup Steps
===========
.. note::
All the steps below are executed from the sandbox folder created in the
second step.
#. Prepare the development environment
.. tab:: Windows
On Windows:
#. Open the Command Prompt or other terminal application of choice
#. Activate the XTC environment:
.. code-block:: console
call "C:\Program Files\XMOS\XTC\15.3.1\SetEnv.bat"
.. tab:: Linux and macOS
On Linux and macOS:
#. Open a terminal
#. Activate the XTC environment using *SetEnv*
.. code-block:: console
source /path/to/xtc/tools/SetEnv
#. Create a sandbox folder with the command below:
.. code-block:: console
mkdir lib_audio_dsp_sandbox
#. Clone the library inside *lib_audio_dsp_sandbox* using SSH (if you
have shared your keys with Github) or HTTPS:
.. code-block:: console
cd lib_audio_dsp_sandbox
# with SSH
git clone git@github.com:xmos/lib_audio_dsp.git
# without SSH
git clone https://github.com/xmos/lib_audio_dsp.git
For troubleshooting SSH issues, please see this
`Github guide <https://docs.github.com/en/authentication/troubleshooting-ssh>`_.
#. Get the lib_audio_dsp library dependencies inside *lib_audio_dsp_sandbox*.
This step can take several minutes.
.. tab:: Windows
On Windows:
.. code-block:: console
cd lib_audio_dsp/examples/app_simple_audio_dsp_integration
cmake -B build -G Ninja
cd ../../..
.. tab:: Linux and macOS
On Linux and macOS:
.. code-block:: console
cd lib_audio_dsp/examples/app_simple_audio_dsp_integration
cmake -B build
cd ../../..
#. Create a Python virtualenv inside *lib_audio_dsp_sandbox*, and install
lib_audio_dsp and it's requirements.
.. note::
Make sure to use the same Python version as the the recommended in the :ref:`Software Requirements <sw_reqs>` section.
.. tab:: Windows
On Windows:
.. code-block:: console
python -m venv .venv
call .venv/Scripts/activate.bat
pip install -e ./lib_audio_dsp/python
.. tab:: Linux and macOS
On Linux and macOS:
.. code-block:: console
python3 -m venv .venv
source .venv/bin/activate
pip install -e ./lib_audio_dsp/python
#. Connect an XK-EVK-XU316 using both USB ports
#. The examples are presented as a Jupyter notebook for interactive development.
Install Juptyer notebooks into the Python virtual environment with the command:
.. code-block:: console
pip install notebook==7.2.1
#. Open the notebook by running from *lib_audio_dsp_sandbox* the following
command:
.. code-block:: console
jupyter notebook lib_audio_dsp/examples/app_simple_audio_dsp_integration/dsp_design.ipynb
If a blank screen appears or nothing opens, then copy the link starting with
http://127.0.0.1/ from the terminal into the browser. The top level Jupyter
notebook page should open, as can be seein in :numref:`top_level_notebook`.
.. _top_level_notebook:
.. figure:: ../images/jupyter_notebook_top_level.png
:width: 25%
Top-level page of the Jupyter Notebook
#. Run all the cells from the browser. From the menu at the top of the page
click *Run -> Run all cells* (:numref:`run_all_cells`).
This creates the pipeline and builds the app. Wait for all the cells to
finish.
.. _run_all_cells:
.. figure:: ../images/jupyter_notebook_run_tests.png
:width: 80%
Run menu of the Jupyter Notebook
Once finished, the setup phase is complete.
The notebook should look like as in the example on :numref:`run_ok`.
.. _run_ok:
.. figure:: ../images/config_ok.png
:width: 100%
Run Success of the Jupyter Notebook
If there are any configuration or compilation errors, they will be displayed in the notebook in
the *Build and run* cell, as in the example on :numref:`run_error`.
.. _run_error:
.. figure:: ../images/config_error.png
:width: 100%
Run Error of the Jupyter Notebook
Once the setup phase is complete, the user can proceed to either creating a custom pipeline,
mapping the pipeline to audio input or output sources, or deploying the pipeline to the xcore.
The :ref:`Using the Tool <using_the_tool>` section describes how to achieve this.
Running a notebook after the first installation
===================================================
If running the notebook after the initial configuration, the following steps are
required:
#. Configure the settings below, using the instructions in the :ref:`Setup
Steps<all_steps>` section:
* Enable the XTC tools: the installation can be tested by running the command
``xrun --version`` from the terminal. If the command is not found, the XTC
tools are not installed correctly.
* From your sandbox, enable the Python Virtual Environment and print the
path to the virtual environment directory. This can be done with the
following commands:
.. tab:: Windows
On Windows:
.. code-block:: console
call .venv/Scripts/activate.bat
echo %VIRTUAL_ENV%
.. tab:: Linux and macOS
On Linux and macOS:
.. code-block:: console
source .venv/bin/activate
echo $VIRTUAL_ENV
#. From the ``lib_audio_dsp_sandbox`` folder, open the notebook by running:
.. code-block:: console
jupyter notebook lib_audio_dsp/examples/app_simple_audio_dsp_integration/dsp_design.ipynb

View File

@@ -0,0 +1,427 @@
.. _using_the_tool:
Using the Tool
##############
In this section the basic operation of the tools provided by lib_audio_dsp is
described.
This document takes the user through three scenarios,
illustrated by way of the included example `app_simple_audio_dsp_integration`,
which may be found in the `examples` directory in `lib_audio_dsp`.
These scenarios are:
- Creating a pipeline
- Tuning and simulating a pipeline
- Deploying pipeline code onto the xcore.
The steps in this guide should be executed in a `Jupyter Notebook`_.
Creating a Pipeline
===================
A simple yet useful DSP pipeline that could be made is a bass and treble control
with output limiter. In this design the product will stream real time audio
boosting or suppressing the treble and bass and then limiting the output
amplitude to protect the output device.
The DSP pipeline will perform the processes shown in :numref:`bass_treble_pipeline`.
.. _bass_treble_pipeline:
.. figure:: ../images/bass_treble_limit.drawio.png
:width: 100%
The target pipeline
The first step is to create an instance of the
:py:class:`Pipeline<audio_dsp.design.pipeline.Pipeline>`
class. This is the top level class which
will be used to create and tune the pipeline. On creation the number of inputs
and sample rate must be specified.
.. code-block:: python
from audio_dsp.design.pipeline import Pipeline
pipeline, inputs = Pipeline.begin(
1, # Number of pipeline inputs.
fs=48000 # Sample rate.
)
The ``Pipeline`` object can now be used to add DSP stages. For high shelf and low
shelf use :ref:`Biquad_stage` and for
the limiter use :ref:`LimiterPeak_stage`.
For a full list of available DSP stages, see the :ref:`dsp_stages_list`.
.. code-block:: python
from audio_dsp.design.pipeline import Pipeline
from audio_dsp.stages import *
p, inputs = Pipeline.begin(1, fs=48000)
# i is a list of pipeline inputs. "lowshelf" is a label for this instance of Biquad.
# The new variable x is the output of the lowshelf Biquad
x = p.stage(Biquad, inputs, "lowshelf")
# The output of lowshelf "x" is passed as the input to the
# highshelf. The variable x is reassigned to the outputs of the new Biquad.
x = p.stage(Biquad, x, "highshelf")
# Connect highshelf to the limiter. Labels are optional, however they are required
# if the stage will be tuned later.
x = p.stage(LimiterPeak, x)
# Finally connect to the output of the pipeline.
p.set_outputs(x)
p.draw()
:numref:`generated_pipeline_diagram` demonstrates the output of the Jupyter Notebook when the above snippet was executed.
The Jupyter Notebook will illustrate the designed pipeline. For information on creating more complex
pipeline topologies, see :ref:`complex_pipelines`.
.. _generated_pipeline_diagram:
.. figure:: ../images/pipeline_diagram.png
:width: 25%
Generated pipeline diagram
Tuning and simulating a pipeline
================================
Each stage contains a number of designer methods which can be identified as they
have the ``make_`` prefix. These can be used to configure the stages. The stages
also provide a ``plot_frequency_response()`` method which shows the magnitude
and phase response of the stage with its current configuration :numref:`freq_responce_bq_diagram`.
The two biquads created above will have a flat frequency response until they are tuned. The code
below shows how to use the designer methods to convert them into the low shelf
and high shelf that is desired. The individual stages are accessed using the
labels that were assigned to them when the stage was added to the pipeline.
.. code-block:: python
# Make a low shelf with a centre frequency of 200 Hz, q of 0.7 and gain of +6 dB
p["lowshelf"].make_lowshelf(200, 0.7, 6)
p["lowshelf"].plot_frequency_response()
# Make a high shelf with a centre frequency of 4000 Hz, q of 0.7 and gain of +6 dB
p["highshelf"].make_highshelf(4000, 0.7, 6)
p["highshelf"].plot_frequency_response()
.. _freq_responce_bq_diagram:
.. figure:: ../images/frequency_response.png
:width: 100%
Frequency response of the biquads (low shelf left, high shelf right)
For this tutorial the default settings for the limiter will provide adequate performance.
Code Generation
===============
With an initial pipeline complete, it is time to generate the xcore source code
and run it on a device. The code can be generated using the
:py:meth:`generate_dsp_main() <audio_dsp.design.pipeline.generate_dsp_main>`
function.
.. code-block:: python
from audio_dsp.design.pipeline import generate_dsp_main
generate_dsp_main(p)
The reference application should then provide instructions for compiling the
application and running it on the target device.
.. note::
`Application Note AN02014 <https://www.xmos.com/file/an02014-integrating-dsp-into-the-xmos-usb-reference-design/>`_
discusses integrating a DSP pipeline into the XMOS USB Reference Design.
The :py:meth:`generate_dsp_main() <audio_dsp.design.pipeline.generate_dsp_main>`
function will cause the tuned DSP pipeline to run on the xcore device,
where it can be used to stream audio.
The next step is to iterate on the design and tune it to perfection.
One option is to repeat the steps described above, regenerating the
code with new tuning values until the performance requirements are satisfied.
.. _complex_pipelines:
Designing Complex Pipelines
===========================
The audio dsp library is not limited to the simple linear pipelines shown above.
Stages can scale to take an arbitrary number of inputs, and the outputs of each
stage can be split and joined arbitrarily.
When creating a new DSP pipeline, the initialiser returns the pipeline input channels as
an instance of
:py:class:`StageOutputList<audio_dsp.design.stage.StageOutputList>`, a list-like container of
:py:class:`StageOutput<audio_dsp.design.stage.StageOutput>`.
When adding a new stage to the pipeline, a ``StageOutputList`` is used to pass the stage inputs.
The stage initialiser returns a new instance of ``StageOutputList`` containing its outputs.
To select specific channels from a ``StageOutputList`` to pass to another
stage, standard Python indexing can be used. Channels from multiple instances of
``StageOutputList`` can be combined by using the ``+`` operator.
The below shows an example of how this could work with a pipeline with 7 inputs.
The pipeline graph is shown in :numref:`7_chan_biquad_pipeline`.
.. code-block:: python
from audio_dsp.design.pipeline import Pipeline
from audio_dsp.stages import Biquad
# start with 7 input channels
p, inputs = Pipeline.begin(7, fs=48000)
# pass the first 2 inputs to a 2-channel Biquad
i0 = p.stage(Biquad, inputs[0:2])
# pass the third input (index 2) to a 1-channel biquad
i1 = p.stage(Biquad, inputs[2])
# pass the inputs at index 3, 5, and 6 to a 3 channel biquad
i2 = p.stage(Biquad, inputs[3, 5, 6])
# pass all of i0 and i1, as well as the first channel in i2
# to create a 4 channel biquad
i3 = p.stage(Biquad, i0 + i1 + i2[0])
# The pipeline output has 6 channels:
# - all four i3 channels
# - the 2nd and 3rd channel from i2
p.set_outputs(i3 + i2[1:])
.. _7_chan_biquad_pipeline:
.. figure:: ../images/complex_pipelines/7_chan_biquad_pipeline.svg
:width: 44.9%
A pipeline splitting and combining inputs.
.. raw:: latex
\newpage
In order to split a signal path, a :ref:`Fork_stage` stage
should be used. This takes a count parameter that specifies how many times to duplicate each input to
the ``Fork``. The code block below shows how the signal chain can be forked.
The pipeline graph is shown in :numref:`fork_pipeline`.
.. code-block:: python
from audio_dsp.design.pipeline import Pipeline
from audio_dsp.stages import Fork
p, inputs = Pipeline.begin(1, fs=48000)
# fork the input to create a 2 channel signal
x = p.stage(Fork, inputs, count=2)
# fork again to create a 4 channel signal
x = p.stage(Fork, x, count=2)
# there are now 4 channels in the pipeline output
p.set_outputs(x)
.. _fork_pipeline:
.. figure:: ../images/complex_pipelines/fork_pipeline.svg
:width: 16%
A pipeline with Fork stages.
.. raw:: latex
\newpage
As the pipeline grows it may end up consuming more MIPS than are available on a
single xcore thread. The pipeline design interface allows adding additional
threads using the
:py:meth:`next_thread() <audio_dsp.design.pipeline.Pipeline.next_thread>`
method of the ``Pipeline`` instance. Each thread
in the pipeline represents an xcore hardware thread. Do not add more threads
than are available in your application. The maximum number of threads that
should be used, if available, is five. This limitation is due to the architecture of the xcore
processor.
The pipeline graph is shown in :numref:`multi_thread_pipeline`.
.. code-block:: python
# thread 0
i = p.stage(Biquad, i)
# thread 1
p.next_thread()
i = p.stage(Biquad, i)
# thread 2
p.next_thread()
i = p.stage(Biquad, i)
.. _multi_thread_pipeline:
.. figure:: ../images/complex_pipelines/multi_thread_pipeline.svg
:width: 16.5%
A multi-threaded DSP pipeline.
.. raw:: latex
\newpage
When using multiple threads, the signal paths must cross the same number of
threads to reach the output. Failure to do this will result in threads blocking
one another, and a reduced pipeline throughput.
The pipeline below is not acceptable, as the
first channel crosses 2 threads, whilst the second channel only crosses one.
The pipeline graph is shown in :numref:`thread_crossings_bad`.
.. code-block:: python
p, inputs = Pipeline.begin(2, fs=48000)
# inputs[1] is not used on thread 0
x1 = p.stage(Biquad, inputs[0])
p.next_thread()
# inputs[1] first used on thread 1
x = p.stage(Biquad, x1 + inputs[1])
p.set_outputs(x)
.. _thread_crossings_bad:
.. figure:: ../images/complex_pipelines/thread_crossings_bad.svg
:width: 21.2%
A bad DSP pipeline with differing thread crossings. The first biquad input
crosses 2 threads, the second biquad input only crosses 1 thread.
.. raw:: latex
\newpage
To ensure signal paths cross the same number of threads, ``Bypass`` Stages
can be used, as shown in the example below.
The pipeline graph is shown in :numref:`thread_crossings_bypass`.
.. code-block:: python
p, inputs = Pipeline.begin(2, fs=48000)
# both inputs are not used on this thread
x1 = p.stage(Biquad, inputs[0])
x2 = p.stage(Bypass, inputs[1])
p.next_thread()
x = p.stage(Biquad, x1 + x2)
p.set_outputs(x)
.. _thread_crossings_bypass:
.. figure:: ../images/complex_pipelines/thread_crossings_bypass.svg
:width: 29.9%
A good DSP pipeline with thread crossings using Bypass stages. Both biquad inputs cross 2 threads.
.. raw:: latex
\newpage
Parallel thread paths are also permitted. In the below case, the inputs
are passed to separate threads before being used in a third.
The pipeline graph is shown in :numref:`thread_crossings_parallel`.
.. code-block:: python
p, inputs = Pipeline.begin(2, fs=48000)
x1 = p.stage(Biquad, inputs[0])
p.next_thread()
x2 = p.stage(Bypass, inputs[1])
p.next_thread()
# x1 and x2 have both crossed 1 thread already
x = p.stage(Biquad, x1 + x2)
p.set_outputs(x)
.. _thread_crossings_parallel:
.. figure:: ../images/complex_pipelines/thread_crossings_parallel.svg
:width: 30.7%
A good DSP pipeline with parallel thread paths. Both biquad inputs cross 1 thread before being combined in a third.
.. raw:: latex
\newpage
.. _json_format:
DSP Pipeline JSON Format
========================
For integrating with external tools, such as GUIs, the DSP pipeline can
be exported to a JSON format. To define and enforce the JSON schema,
`pydantic <https://pypi.org/project/pydantic/>`_ models of each stage are
used. Pydantic provides type hinting and JSON schema verification to ensure that
DSP pipelines are valid.
To convert a pipeline to JSON, the following steps can be followed:
.. code-block:: python
from audio_dsp.design.pipeline import Pipeline
from audio_dsp.design.parse_json import DspJson, pipeline_to_dspjson, make_pipeline
from pathlib import Path
from audio_dsp.stages import Biquad
p, inputs = Pipeline.begin(1, fs=48000)
x = p.stage(Biquad, inputs)
p.set_outputs(x)
# Generate the pydantic model of the pipeline
json_data = pipeline_to_dspjson(p)
# write the JSON data to a file
filepath = Path("pipeline.json")
filepath.write_text(json_data.model_dump_xdsp())
To read a pipeline from a JSON file, the following steps can be followed:
.. code-block:: python
# read the JSON data back to a DSP pipeline
json_text = filepath.read_text()
json_data = DspJson.model_validate_json(json_text)
p = make_pipeline(json_data)
The JSON file can be edited in an external editor. If the edited JSON file
is not valid, an exception will be raised during ``model_validate_json``
with a description of the error.

View File

@@ -0,0 +1,238 @@
Summary of the xcore architecture
=================================
A basic understanding of the xcore architecture is required in order to understand the consequences of various design
choices that can be made in the DSP pipeline.
An xcore application will consist of 1 or more xcore chips connected together via a communication fabric (the
XLink). Each xcore chip contains 2 or more tiles; a tile is an independent processor with its own memory. A tile cannot
read or write the memory of another tile. Each tile contains 8 logical cores; a logical core is an independent thread
of execution that will run some application code. Each tile also has 32 chanends available for allocation; connecting
2 chanends forms a channel, which allows for synchronous communication between any 2 logical cores in the system (even between tiles or
packages).
In its default configuration, an xcore.ai chip will operate at 600MHz; this means that each tile executes instructions
at a rate of 600MIPS. This is shared between the 8 logical cores by multiplexing the execution across 5 time slots. Each
thread can consume at most 1 time slot per scheduler cycle. The consequence of this is that for applications with up to
5 threads, each thread operates at 120MIPS (600/5). If there are over 5 threads then this number can be reduced down to
75MIPS (600/8). If any of the threads modify their priority mode then this can reduce the available MIPS even further;
high-priority threads are always guaranteed a slot in the scheduler on each cycle.
================= ===============
Term Definition
================= ===============
xcore.ai A chip containing 2 or more tiles.
Tile A single processor with some memory.
Logical Core 1 of the 8 threads available in each tile.
Chanend The physical hardware used by a logical core to create a channel. There are 32 available per tile.
Channel The bidirectional communication pathway that is created when 2 chanends are connected.
================= ===============
For more information about the xcore architecture, consult `The XMOS XS3 Architecture`_ and the data sheet for your
package.
The Architecture of the Generated Pipeline
==========================================
:numref:`dsp-class-label` shows the relationship between the classes in an application with a generated DSP pipeline. A
class in this context refers to a C struct and the functions that operate on it.
.. _dsp-class-label:
.. figure:: ../images/dsp_class.drawio.png
:width: 100%
Class diagram of a lib_audio_dsp application
The application package contains *Audio Source*, *Audio Sink* and *Control* classes.
The *Audio Source* and *Audio Sink* are responsible
for producing and consuming audio at the rate required by the DSP pipeline. The *Control* is responsible for implementing
any application specific dynamic control of the DSP pipeline; this is optional and will only be present where run time
control is used. These are in the *Application* block as they will be unique for each application. *Audio Source*, *Audio
Sink*, and *Control* make use of the classes in lib_audio_dsp; all make use of a pointer to a shared ``adsp_pipeline_t`` (as
shown by the aggregation relationships (hollow diamond) in :numref:`dsp-class-label`). lib_audio_dsp presents a thread
safe API, allowing *Audio Source*, *Audio Sink* and *Control* to exist on separate threads if desired. However, they must all
exist on the same tile in order to access the shared ``adsp_pipeline_t``.
The lib_audio_dsp repository represents the classes from this library.
These APIs are documented fully in the :ref:`dsp_integration_control` section.
The *Generated Pipeline* package represents the classes and objects which will be generated from the user's specified
DSP pipeline design. :numref:`dsp-class-label` shows that ``adsp_generated_auto`` is composed of (filled diamond) the
``adsp_pipeline_t`` and multiple ``module_instance_t``. Therefore, the generated pipeline is responsible for allocating
the memory for all the stages in the pipeline and also initialising each stage. The generated pipeline also creates
multiple threads (labelled ``dsp_threadX`` in :numref:`dsp-class-label`), each of which will have been uniquely
generated for the DSP pipeline that has been designed. The generated pipeline will always require at least 1 thread to
run the DSP on; it is not possible to generate a DSP pipeline that can be executed inline on an existing thread. It is
also not possible to split the DSP threads across more than 1 tile, because all threads access a shared ``adsp_pipeline_t``
object.
To summarise, the generated DSP pipeline will consume the number of threads specified in the design (at least 1). At
least one other thread on the same tile must be available to exchange audio with the DSP pipeline.
Resource usage of the Generated Pipeline
========================================
The resources that are consumed by the generated DSP pipeline are threads, chanends, and memory. Each DSP thread also has a finite number of instructions per sample that are available for DSP. It is the responsibility of the DSP designer to ensure that this limit is not exceeded on any of the threads.
.. _design_chanend_usage_section:
Chanend Usage
-------------
The following snippet of Python shows a DSP design; the pipeline diagram for the snippet is shown
in :numref:`design_resources_gv_label`. This design splits 4 DSP stages amongst 3 threads. Threads 0 and 1 operate
on the pipeline inputs in parallel. Thread 2 receives its inputs from threads 0 and 1. The pipeline output comes from
thread 1.
The generated DSP threads and the APIs for exchanging inputs with the pipeline all use channels to communicate audio.
.. literalinclude:: ../../../test/pipeline/doc_examples/design_guide_resources.py
:language: python
:start-after: # start example
:end-before: # end example
.. _design_resources_gv_label:
.. figure:: ../images/design_guide_resources.gv.png
:width: 20%
Output of `Pipeline.draw()` for the example pipeline
:numref:`design_resources_chanends_label` shows how the chanends are allocated for this design. A channel (2 chanends)
is allocated for every connection from one thread to another. Thread 2 receives data from thread 0 and 1, therefore it
has 2 input channels. It only outputs to 1 thread (end) so has 1 output channel.
If multiple data channels are passed from 1 thread to another (e.g. 3 channels from thread 1 to 2) this still only
consumes a single xcore channel (2 chanends) as all the data channels are sent over the same xcore channel.
For a simple linear pipeline, the chanend usage will be :math:`2 * num_dsp_threads + 2`. For pipelines with parallel
threads the usage will be higher, as shown in :numref:`design_resources_chanends_label` where 10 chanends (5 channels) are
used for 3 DSP threads.
.. _design_resources_chanends_label:
.. figure:: ../images/chanends.drawio.png
:width: 100%
Chanend usage for the example pipeline
.. _design_thread_usage_section:
Thread Usage
------------
Thread usage of the DSP pipeline is discussed in the sections above. Understanding the thread usage of your application
is a manual process. The application designer must have an understanding of how many threads are in use in their
application, including in the DSP pipeline, to ensure that the limit of 8 is not exceeded (of which up to 5 may be DSP threads).
If this limit is exceeded the xcore will trap when the application attempts to fork a ninth thread.
.. _design_memory_usage_section:
Memory Usage
------------
All memory used in the generated DSP pipeline is statically allocated and therefore known at compile time. The Python
design API cannot assist in understanding the memory usage of your application. The memory report which is displayed
when compiling the application must be consulted to see the memory used. This value will include the generated DSP
pipeline as well as any other application code that is running on the tile.
.. _design_mips_usage_section:
MIPS Usage
----------
In order to operate in a real time audio system it is critical that each thread in the DSP pipeline can complete
execution in less time than the sample period (or frame period if the frame size is greater than 1). It is this
constraint that requires the DSP to be split into pipelined threads. If a thread is overloaded, the DSP pipeline will
consume and produce samples at a slower rate than expected. This could cause the source and sink threads to block and
miss timing. The current version of lib_audio_dsp provides only limited support for measuring the MIPS usage of each
thread.
Each thread measures the total number of system ticks (periods of the system clock, by default a 100MHz clock) that pass
while it is doing work and stores the maximum value that has occurred since boot. This measurement can be used to get
an estimate of the threads' MIPS utilisations. To access this value, the function ``adsp_auto_print_thread_max_ticks()``
and ``adsp_auto_fast_print_thread_max_ticks()`` are generated along with the other generated pipeline functions
("auto" may be replaced with a custom pipeline identifier if specified). Calling one of these functions on the same tile
as the pipeline will print the measured value. ``adsp_auto_print_thread_max_ticks()`` should be called from the control
thread of the application, and uses the control command interface to safely retrieve and print the values.
``adsp_auto_fast_print_thread_max_ticks()`` should be called from the DSP thread itself, and directly access the
pipeline object to retrieve the values. This function is faster than the previous one, but is likely to block the DSP
thread for a short time while it prints the values, causing future calls to have higher max ticks values.
Both functions are implemented with ``printf``, so the output will only be visible when connected to the device with ``xrun`` or ``xgdb``.
An example call to ``adsp_auto_fast_print_thread_max_ticks()`` is shown below.
.. code-block:: c
// This code is called from the application thread that sends samples to the DSP
static uint32_t frame_counter = 0;
frame_counter += 1;
adsp_pipeline_source(m_dsp, dsp_input);
adsp_pipeline_sink(m_dsp, dsp_output);
if (frame_counter == fs){
// Print the thread ticks after 1s, this will likely block
// and cause future ticks to be incorrect
adsp_auto_fast_print_thread_max_ticks(m_dsp);
}
The number of available ticks on each thread depends on the frame size and sample rate of the data. For example, given
that the system clock runs by default at 100MHz, if the sample rate is 48000 Hz and frame size is 1 then the available
ticks will be :math:`1 * 100e6/48000 = 2083 ticks`. Below is an example output from ``adsp_auto_print_thread_max_ticks()``
for a pipeline with 4 threads::
DSP Thread Ticks:
0: 1800
1: 181
2: 67
3: 93
The number that is displayed is the worst case that has happened since boot. This is not necessarily the absolute worst
case as some stages have data dependent execution time. Therefore, it is recommended to play an audio signal through the
pipeline with varying amplitude and frequencies before measuring the thread MIPS.
Troubleshooting resource issues
===============================
Tile exceeds memory limit
-------------------------
Memory available check will report "FAILED" during linking. The :ref:`design_memory_usage_section` section describes how
memory is allocated in the DSP pipeline. Recommended steps:
#. Remove all stages from the pipeline.
#. Add them back one at a time and take note of the memory usage of each stage.
#. Consult the documentation for the problematic stages and see if its memory usage is configuration dependent.
Moving stages between threads will not impact the memory usage as all threads are on the same tile.
Tile exceeds available chanends
-------------------------------
If a tile attempts to allocate too many chanends it will raise an illegal resource exception and cease execution. This
can be detected easily with xgdb or xrun as it will print the following message::
Unhandled exception: ILLEGAL_RESOURCE
The :ref:`design_chanend_usage_section` section describes how chanends are used within the DSP pipeline. Resolving this
problem will require either redesigning the DSP or the application that runs on the same tile to use fewer chanends.
Exchanging audio with the DSP pipeline blocks for too long
----------------------------------------------------------
``adsp_pipeline_sink`` or ``adsp_pipeline_source`` will block until data is available. The :ref:`design_mips_usage_section`
section describes how to ensure the DSP pipeline meets timing. Identifying this particular issue will depend on the rest
of the application. The result could be either dropped samples that are audible in the output or a complete application crash.
Tile exceeds available threads
------------------------------
If a tile attempts to fork too many threads it will raise an illegal resource exception and cease execution. This can be
detected easily with xgdb or xrun as it will print the following message::
Unhandled exception: ILLEGAL_RESOURCE
The :ref:`design_thread_usage_section` section describes how threads are used within the DSP pipeline. Resolving this
problem will require either redesigning the DSP or the application that runs on the same tile to use fewer threads.

View File

@@ -0,0 +1,19 @@
.. raw:: latex
\newpage
.. _design_guide_section:
Design Guide
############
This section will cover the details of how the xcore DSP pipeline is generated from the Python description. This should
enable the reader to debug their pipeline when issues arise and understand the resource usage of a generated DSP
pipeline. This is an advanced guide intended for users who wish for a deeper understanding of the generated DSP pipeline
that they have created. The accompanying tool and component guides should be consulted for the basic process of using
this tool.
.. toctree::
:maxdepth: 2
design_guide

View File

@@ -0,0 +1,39 @@
.. raw:: latex
\newpage
.. _dsp_components_section:
DSP Components
##############
`lib_audio_dsp` provides many common signal processing functions optimised
for xcore. These can be combined together to make complex audio pipelines
for many different applications, such as home audio, music production,
voice processing, and AI feature extraction.
The library is split into 2 levels of API: DSP stages and DSP modules.
Both APIs provide similar DSP functionality, but are suited to different
use cases.
The higher-level APIs are called :ref:`dsp_stages_section`. These stages are designed
to work with the Python DSP pipeline tool. This tool allows developers
to quickly and easily create, test, and deploy DSP pipelines without
needing to write a lot of code. By using DSP stages, the user can build
complex audio processing workflows in a short amount of time, making it
ideal for rapid prototyping and development.
The lower-level APIs are called :ref:`dsp_modules_section`. They are meant to be
used as an API directly in cases where the Python DSP pipeline tool is
not used. These modules can be useful when integrating DSP functionality into an
existing system, or as a starting point for creating bespoke DSP
functions.
.. toctree::
:maxdepth: 1
gen/stages
modules
q_format
precision
latency

View File

@@ -0,0 +1,21 @@
Latency
=======
The latency of the DSP pipeline is dependent on the number of threads. By default, the DSP pipeline
is configured for one sample of latency per thread. All current DSP modules have zero inbuilt
latency (except where specified e.g. delay stages). For pipelines that fit on a single thread,
this means the total pipeline latency is 1 sample.
The pipeline can also be configured to use a higher frame size. This increases latency, but can
reduce compute for simple functions, as the function overhead is shared. For a pipeline consisting of just biquads:
.. table::
:widths: 15, 15, 15, 15, 15
========== ======= ================== =================== ======================
Frame size Latency % thread per frame % thread per sample Max biquads per thread
========== ======= ================== =================== ======================
1 1 4.0% 4.0% 25
8 8 13.3% 1.7% 60
========== ======= ================== =================== ======================

View File

@@ -0,0 +1,53 @@
DSP Modules List
================
This a list of all the modules that can be used independently without the DSP pipeline tool:
- :ref:`biquad_filters`
* :ref:`Biquad`
* :ref:`BiquadSlew`
* :ref:`CascadedBiquads`
* :ref:`CascadedBiquads16`
- :ref:`drc`
* :ref:`EnvelopeDetectorPeak`
* :ref:`EnvelopeDetectorRMS`
* :ref:`Clipper`
* :ref:`LimiterPeak`
* :ref:`HardLimiterPeak`
* :ref:`LimiterRMS`
* :ref:`CompressorRMS`
* :ref:`CompressorSidechain`
* :ref:`CompressorSidechainStereo`
* :ref:`NoiseGate`
* :ref:`NoiseSuppressorExpander`
- :ref:`geq`
* :ref:`GraphicEq10b`
- :ref:`fir`
* :ref:`FirDirect`
* :ref:`FirBlockTD`
* :ref:`FirBlockFD`
- :ref:`reverb`
* :ref:`ReverbRoom`
* :ref:`ReverbRoomStereo`
* :ref:`ReverbPlateStereo`
- :ref:`signal_chain`
* :ref:`Adder`
* :ref:`Subtractor`
* :ref:`FixedGain`
* :ref:`Mixer`
* :ref:`VolumeControl`
* :ref:`Delay`
* :ref:`SwitchSlew`
* :ref:`Crossfader`

View File

@@ -0,0 +1,23 @@
Precision
=========
.. note::
For fixed point Q formats this document uses the format QM.N, where M is the number of bits
before the decimal point (excluding the sign bit), and N is the number of bits after the decimal
point. For an int32 number, M+N=31.
By default, the signal processing in the audio pipeline is carried out at 32 bit fixed point
precision in Q4.27 format. Assuming a 24 bit input signal in Q0.24 format, this gives 4 bits of internal headroom in
the audio pipeline, which is equivalent to 24 dB. The output of the audio pipeline will be clipped back to Q0.24 before
returning. For more precision, the pipeline can be configured to run with no headroom
in Q0.31 format, but this requires manual headroom management. More information on setting the Q
format can be found in the :ref:`library_q_format_section` section.
DSP algorithms are implemented either on the XS3 CPU or VPU (vector processing unit).
CPU algorithms are typically implemented as 32-bit x 32-bit operations into 64-bit results and
accumulators, before rounding back to 32-bit outputs.
The VPU allows for 8 simultaneous operations, with a small cost in precision. VPU algorithms are
typically implemented as 32-bit x 32-bit operations into 34-bit results and 40-bit accumulators,
before rounding back to 32-bit outputs.

View File

@@ -0,0 +1,64 @@
.. _library_q_format_section:
################
Library Q Format
################
.. note::
For fixed point Q formats this document uses the format QM.N, where M is the number of bits
before the decimal point (excluding the sign bit), and N is the number of bits after the decimal
point. For an int32 number, M+N=31.
By default, the signal processing in the audio pipeline is carried out at 32 bit fixed point
precision in Q4.27 format. Assuming a 24 bit input signal in Q0.24 format, this gives 4 bits of
internal headroom in the audio pipeline.
Most modules in this library assume that the signal is in a specific global Q format.
This format is defined by the ``Q_SIG`` macro. An additional macro for the signal exponent,
``SIG_EXP`` is defined, where ``SIG_EXP = - Q_SIG``.
.. doxygendefine:: Q_SIG
.. doxygendefine:: SIG_EXP
To ensure optimal headroom and noise floor, the user should ensure that signals are in the correct
Q format before processing. Either the input Q format can be converted to ``Q_SIG``, or ``Q_SIG``
can be changed to the desired value. When using the DSP pipeline too, the Q format is automatically
converted. The user should ensure that 0 dBFS is aligned with the maximum int32 value.
.. note::
Not using the DSP pipeline tool means that Q formats
will not automatically be managed, and the user should take care to ensure they have the correct
values for optimum performance and signal level.
For example, for more precision, the pipeline can be configured to run with no headroom
in Q0.31 format, but this would require manual headroom management (e.g. reducing the signal level
before a boost to avoid clipping).
If not using the DSP pipeline tool, the APIs below provide conversions between ``Q_SIG`` and Q0.31
in a safe and optimised method, assuming that 0 dBFS is aligned with the maximum int32 value.
.. doxygenfunction:: adsp_from_q31
.. doxygenfunction:: adsp_to_q31
To convert a 16 bit audio signal into the Q_SIG format and back, the following functions can be used:
.. code-block:: c
// input signal in Q0.15 format
int16_t signal = 1234;
// Convert to Q0.31
int32_t sig_32 = ((int32_t)signal) << 16;
// Convert to Q_SIG
sig_32 = adsp_from_q31(sig_32);
// Do some processing here
// Convert back to Q0.31
sig_32 = adsp_to_q31(sig_32);
// Convert back to 16 bit without dithering
signal = (int16_t)(sig_32 >> 16);

View File

@@ -0,0 +1,140 @@
.. _run_time_control_guide_walkthrough:
Control Interface Details
=========================
Each stage that is included in the generated DSP pipeline has its own state which it will maintain as it processes
audio. It also has a structure that contains its configuration parameters, and a control state variable
which is used to communicate between the DSP and control threads. Threads that wish to read or write to the
configuration of a stage use the control API that is discussed below.
For a write command, the controlling thread will check that a command is not ongoing by querying the control state of
the stage. If the stage is not processing a control command, then the control thread will update the configuration struct
for the stage and write to the control state variable that new parameters are available. When the DSP thread next gets
an opportunity, the stage will see that the parameters have been updated and update its internal state to match. When
this is complete the control state variable will be cleared.
For a read command the process is similar. The control thread requests a read by updating the control state variable.
The stage will see this and update the configuration struct with the latest value. The stage will notify the control thread, via the
control state variable, that it has completed the request.
The control API ensures thread safety through the use of the ``adsp_controller_t`` struct. As long as each thread uses
a unique instance of ``adsp_controller_t`` then the control APIs will return ``ADSP_CONTROL_BUSY`` if a command that was
initialised by another ``adsp_controller_t`` is ongoing.
Defining a Controllable Pipeline
================================
This section will walk through adding control to a basic DSP pipeline. The following code snippet
describes a simple DSP process with a volume control and a limiter. In the end application the
volume can be set by the application.
This code snippet will generate the pipeline diagram shown in :numref:`run_time_example_fig`.
.. literalinclude:: ../../../test/pipeline/doc_examples/run_time_control.py
:language: python
:start-after: # start example
:end-before: # end example
.. _run_time_example_fig:
.. figure:: ../images/run_time_control.gv.png
:width: 20%
The example pipeline diagram
In this example the tuning methods on the stages in the pipeline are not called which means
the code that is generated will intialise the stages with their default configuration values.
A point of interest in this example is that the ``label`` argument to the pipeline ``stage``
method is set, but only for the volume control stage. The label for the volume control in
this example is ``volume``. After generating the source code for this pipeline, a file will
be created in the specified directory named ``adsp_instance_id_auto.h`` (assuming that the pipeline
identifier has been left as its default value of "auto"). The contents of the generated file are shown below:
.. literalinclude:: ../../../test/pipeline/doc_examples/run_time_dsp/src/dsp/adsp_instance_id_auto.h
:language: C
In this file the macro ``volume_stage_index`` is defined. The value of this macro can be used by
the control interface to find the volume control stage and process control commands. The benefit
of this to an application author is that this header file can be included in the application and
the value of ``volume_stage_index`` will always be correct, even when the pipeline is redesigned.
Writing the Configuration of a Stage
====================================
Each stage type has a set of controllable parameters that can be read or written. A description of
each parameter along with its type and name can be found in the :ref:`dsp_stages_section` section
in the DSP components document. For volume control, there is a command named ``CMD_VOLUME_CONTROL_TARGET_GAIN``
that can be updated at run time to set the volume. This command is defined in the generated header file
``cmds.h`` which will be placed into the build directory at ``src.autogen/common/cmds.h``. ``cmds.h`` contains all
the command IDs for all the stage types that CMake found.
It is also possible to see the available control parameters, along with the values they will be set to, while
designing the pipeline in Python. This can be done using the ``get_config`` method of the stage as shown below.
.. literalinclude:: ../../../test/pipeline/doc_examples/run_time_control.py
:language: python
:start-after: # start config
:end-before: # end config
This will print this dictionary of parameters:
.. literalinclude:: ../../../test/pipeline/doc_examples/run_time_dsp/config.txt
This dictionary does not contain ``CMD_VOLUME_CONTROL_TARGET_GAIN``, but is does contain "target_gain". The final
command name is constructed as ``CMD_{STAGE_TYPE}_{PARAMETER}`` where stage type and parameter should be replaced with
the correct values for each, capitalised. All stages of the same type (e.g. ``VolumeControl``) will have the same
set of parameters.
The format and type of the control parameters for each stage are chosen to optimise processing time on the
DSP thread. For example, ``CMD_VOLUME_CONTROL_TARGET_GAIN`` is not a floating point value in decibels, but rather
a linear fixed point value. For this example we can use the convenience function
``adsp_dB_to_gain()`` which is defined in ``control/signal_chain.h``.
In order to send a control command, the API defined in ``stages/adsp_control.h`` is used. This API is documented in the
:ref:`api_reference_section`, in the :ref:`dsp_control` section. Complete the
following steps:
#. Create a thread that will be updating the DSP configuration. This thread must be on the same tile as the DSP.
#. Create a new ``adsp_controller_t`` from the ``adsp_pipeline_t`` that was initialised for the generated pipeline. If
multiple threads will be attempting control, each thread must have a unique instance of ``adsp_controller_t`` to ensure
thread safety.
#. Initialise a new ``adsp_stage_control_cmd_t``, specifying the instance ID (``volume_stage_index``), the command
ID (``CMD_VOLUME_CONTROL_TARGET_GAIN``), and payload length (``sizeof(int32_t)``).
#. Create the command payload; this will be an ``int32_t`` containing the computed gain. Update the command payload
pointer to reference the payload.
#. Call ``adsp_write_module_config`` until it returns ``ADSP_CONTROL_SUCCESS``. There may be in-progress write or read
commands which have been issued but not completed when starting the new command. In this scenario
the ``adsp_write_module_config`` will return ``ADSP_CONTROL_BUSY`` which means that the attempt to write had no
effect and should be attempted again.
A full example of a control thread that does this is shown below.
.. literalinclude:: ../../../test/pipeline/doc_examples/run_time_dsp/src/main.c
:language: C
:start-after: // start example
:end-before: // end example
Reading the Configuration of a Stage
====================================
In some cases it makes sense to read back the configuration of the stage. Some stages have dynamic values that are
updated as the audio is processed and can be read back to the control thread. Volume control is an example of this
as it will smoothly adjust the gain towards ``CMD_VOLUME_CONTROL_TARGET_GAIN``; the current value of the gain which
is actually being applied can be read by reading from the parameter ``CMD_VOLUME_CONTROL_GAIN``. The API for reading
is largely the same as writing, except the control API will write to the payload buffer.
This code example shows how to read the current ``CMD_VOLUME_CONTROL_GAIN`` parameter from the "volume" stage that
is created in the example above.
.. literalinclude:: ../../../test/pipeline/doc_examples/run_time_dsp/src/main.c
:language: C
:start-after: // start read
:end-before: // end read

View File

@@ -0,0 +1,22 @@
.. raw:: latex
\newpage
.. _run_time_control_guide_section:
Run-Time Control User Guide
###########################
For many applications, the ability to update the DSP configuration at run time will be required. A simple example
would be a volume control where the end product will update the volume setting based on user input. This
DSP library has been designed with use cases like this in mind and the generated DSP pipeline provides an interface for
writing and reading the configuration of each stage.
This document details how to use this interface to extend a DSP application with run-time control
of the audio processing. For a complete example of an application that updates the DSP configuration
based on user input refer to application note AN02015.
.. toctree::
:maxdepth: 1
control_interface_walkthrough

View File

@@ -0,0 +1,19 @@
.. raw:: latex
\newpage
.. _api_reference_section:
API Reference
#############
This section provides a comprehensive guide to the DSP components, their integration into the DSP pipeline, and the run-time control mechanisms.
It also includes an overview of high-level pipeline design principles.
.. toctree::
:maxdepth: 1
stages/index
modules/index
integration/index
pipeline_design

View File

@@ -0,0 +1,7 @@
.. _dsp_control:
#######
Control
#######
.. doxygenfile:: adsp_control.h

View File

@@ -0,0 +1,60 @@
.. _run_time_control_helper_section:
========================
Control Helper Functions
========================
Most DSP Stages have fixed point control parameters. To aid conversion
from typical tuning units (e.g. decibels) to the correct fixed point
format, the helper functions below have been provided.
.. _biquad_helpers:
Biquad helpers
==============
.. doxygenfile:: control/biquad.h
DRC helpers
===========
.. doxygenfunction:: calc_alpha
.. doxygenfunction:: calculate_peak_threshold
.. doxygenfunction:: calculate_rms_threshold
.. doxygenfunction:: rms_compressor_slope_from_ratio
.. doxygenfunction:: peak_expander_slope_from_ratio
.. doxygenfunction:: qxx_to_db
.. doxygenfunction:: qxx_to_db_pow
.. doxygenfile:: control/drc.h
Graphic EQ helpers
==================
.. doxygenfunction:: adsp_graphic_eq_10b_init
.. doxygenfunction:: geq_db_to_gain
Reverb helpers
==============
.. doxygenfile:: control/reverb.h
.. doxygenfile:: control/reverb_plate.h
Signal chain helpers
====================
.. doxygenfile:: control/signal_chain.h

View File

@@ -0,0 +1,15 @@
.. _dsp_integration_control:
Integration and Control
========================
This section covers the API necessary to integrate the generated DSP pipeline into your application, both data and control-wise.
It will introduce the API necessary for converting from high-level DSP parameters, to the ones that can be sent to the DSP pipeline.
.. toctree::
:maxdepth: 1
pipeline
module
control
helpers

View File

@@ -0,0 +1,5 @@
######
Module
######
.. doxygenfile:: adsp_module.h

View File

@@ -0,0 +1,5 @@
########
Pipeline
########
.. doxygenfile:: adsp_pipeline.h

View File

@@ -0,0 +1,169 @@
.. _biquad_filters:
##############
Biquad Filters
##############
.. _Biquad:
=============
Single Biquad
=============
A second order biquadratic filter, which can be used to implement many common second order filters.
The filter had been implemented in the direct form 1, and uses the xcore.ai vector unit to
calculate the 5 filter taps in a single instruction.
Coefficients are stored in Q1.30 format to benefit from the vector unit, allowing for a filter
coefficient range of ``[-2, 1.999]``. For some high gain biquads (e.g. high shelf filters), the
numerator coefficients may exceed this range. If this is the case, the numerator coefficients only
should be right-shifted until they fit within the range (the denominator coefficients cannot become
larger than 2.0 without the poles exceeding the unit circle). The shift should be passed into the API,
and the output signal from the biquad will then have a left-shift applied. This is equivalent to
reducing the overall signal level in the biquad, then returning to unity gain afterwards.
The ``state`` should be initialised to ``0``. The ``state`` and ``coeffs`` must be word-aligned.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_biquad
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.biquad.biquad
:noindex:
.. automethod:: process
:noindex:
.. automethod:: update_coeffs
:noindex:
.. automethod:: reset_state
:noindex:
.. _BiquadSlew:
=====================
Single Slewing Biquad
=====================
This is similar to `Biquad`_, but when the target coefficients are updated it slew the applied
coefficients towards the new values. This can be used for real time adjustable filter control.
If the left-shift of the coefficients changes, this is managed by the ``biquad_slew_t`` object:
.. doxygenstruct:: biquad_slew_t
:members:
.. tab:: C API
.. only:: latex
.. rubric:: C API
Filtering the samples can be carried out using ``adsp_biquad``:
.. code-block:: C
for (int j=0; i < n_samples; i++){
adsp_biquad_slew_coeffs(&slew_state, states, 1);
for (int i=0; i < n_chans; i++){
samp_out[i, j] = adsp_biquad(samp_in[i, j], slew_state.active_coeffs, states[i], slew_state.lsh);
}
}
.. doxygenfunction:: adsp_biquad_slew_coeffs
See also :c:func:`adsp_biquad_slew_init` and :c:func:`adsp_biquad_slew_update_coeffs`.
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.biquad.biquad_slew
:noindex:
.. automethod:: process
:noindex:
.. automethod:: update_coeffs
:noindex:
.. automethod:: reset_state
:noindex:
.. _CascadedBiquads:
================
Cascaded Biquads
================
The cascaded biquad module is equivalent to 8 individual biquad filters connected in series. It
can be used to implement a simple parametric equaliser or high-order Butterworth filters,
implemented as cascaded second order sections.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_cascaded_biquads_8b
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.cascaded_biquads.cascaded_biquads_8
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
.. _CascadedBiquads16:
===================
Cascaded Biquads 16
===================
This extends the CascadedBiquads class to have 16 cascaded filters. However,
The 8 filter C implementation should still be used.
.. tab:: C API
See :ref:`CascadedBiquads`.
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.cascaded_biquads.cascaded_biquads_16
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:

View File

@@ -0,0 +1,497 @@
.. _drc:
#####################
Dynamic Range Control
#####################
Dynamic Range Control (DRC) in audio digital signal processing (DSP) refers to the automatic adjustment of an audio
signal's amplitude to reduce its dynamic range - the difference between the loudest and quietest parts of the audio.
They include compressors, limiters and clippers, as well as the envelope detectors used to detect the signal level.
========================
Attack and Release Times
========================
Nearly all DRC modules feature an attack and release time to control the
responsiveness of the module to changes in signal level. Attack and
release times converted from seconds to alpha coefficients for use in
the the exponential moving average calculation. The shorter the attack or release time, the bigger the alpha. Large
alpha will result in the envelope becoming more reactive to the input samples. Small alpha values will give more
smoothed behaviour. The difference between the input level and the current envelope or gain determines whether the
attack or release alpha is used.
==================
Envelope Detectors
==================
Envelope detectors run an exponential moving average (EMA) of the
incoming signal. They are used as a part of the most DRC components.
They can also be used to implement VU meters and level detectors.
They feature `attack and release times`_ to control the responsiveness
of the envelope detector.
The C struct below is used for all the envelope detector implementations.
.. doxygenstruct:: env_detector_t
:members:
.. _EnvelopeDetectorPeak:
----------------------
Peak Envelope Detector
----------------------
A peak-based envelope detector will run its EMA using the absolute value of the input sample.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_env_detector_peak
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.drc.envelope_detector_peak
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
.. _EnvelopeDetectorRMS:
---------------------
RMS Envelope Detector
---------------------
An RMS-based envelope detector will run its EMA using the square of the
input sample. It returns the mean² in order to avoid a square root.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_env_detector_rms
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.drc.envelope_detector_rms
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
.. _Clipper:
=======
Clipper
=======
A clipper limits the signal to a specified threshold. It is applied
instantaneously, so has no attack or release times.
.. doxygentypedef:: clipper_t
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_clipper
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.drc.clipper
:noindex:
.. automethod:: process
:noindex:
========
Limiters
========
Limiters will reduce the amplitude of a signal when the signal envelope
is greater than the desired threshold. This is similar behaviour to a compressor
with an infinite ratio.
A limiter will run an internal envelope detector to get the signal
envelope, then compare it to the threshold. If the envelope is greater than the
threshold, the applied gain will be reduced. If the envelope is below
the threshold, unity gain will be applied. The gain is run through an EMA
to avoid abrupt changes. The same `attack and release times`_ are used
for the envelope detector and the gain smoothing.
The C struct below is used for all the limiter implementations.
.. doxygenstruct:: limiter_t
:members:
.. _LimiterPeak:
------------
Peak Limiter
------------
A peak limiter uses the :ref:`EnvelopeDetectorPeak` to get an envelope.
When envelope is above the threshold, the new gain is calculated as
``threshold / envelope``.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_limiter_peak
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.drc.limiter_peak
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
.. _HardLimiterPeak:
-----------------
Hard Peak Limiter
-----------------
A hard peak limiter is similar to a :ref:`LimiterPeak`, but will clip
the output if it's still above the threshold after the peak limiter.
This can be useful for a final output limiter before truncating any
headroom bits.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_hard_limiter_peak
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.drc.hard_limiter_peak
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
.. _LimiterRMS:
-----------
RMS Limiter
-----------
A RMS limiter uses the :ref:`EnvelopeDetectorRMS` to calculate an envelope.
When envelope is above the threshold, the new gain is calculated as
``sqrt(threshold / envelope)``.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_limiter_rms
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.drc.limiter_rms
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
===========
Compressors
===========
A compressor will attenuate the signal when the envelope is greater than the
threshold. The input/output relationship above the threshold is defined
by the compressor ``ratio``.
As with a limiter, the compressor runs an internal envelope detector
to get the signal envelope, then compares it to the threshold. If the
envelope is greater than the threshold, the gain will be proportionally reduced
by the ``ratio``, such that it is greater than the threshold by a smaller amount.
If the envelope is below the threshold, unity gain will be applied.
The gain is then run through an EMA to avoid abrupt changes, before being
applied.
The ``ratio`` defines the input/output gradient in the logarithmic domain.
For example, a ratio of 2 will reduce the output gain by 0.5 dB for every
1 dB the envelope is over the threshold.
A ratio of 1 will apply no compression.
To avoid converting the envelope to the logarithmic domain for the gain
calculation, the ratio is converted to the ``slope`` as
``(1 - 1 / ratio) / 2`` . The gain can then be calculated as an
exponential in the linear domain.
The C structs below are used for all the compressors implementations.
.. doxygenstruct:: compressor_t
:members:
.. doxygenstruct:: compressor_stereo_t
:members:
.. _CompressorRMS:
--------------
RMS Compressor
--------------
The RMS compressor uses the :ref:`EnvelopeDetectorRMS` to calculate an
envelope.
When the envelope is above the threshold, the new gain is calculated as
``(threshold / envelope) ^ slope``.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_compressor_rms
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.drc.compressor_rms
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
.. _CompressorSidechain:
------------------------
Sidechain RMS Compressor
------------------------
The sidechain RMS compressor calculates the envelope of one signal and
uses it to compress another signal.
It takes two signals: *detect* and *input*. The envelope of the *detect* signal
is calculated using an internal :ref:`EnvelopeDetectorRMS`.
The gain is calculated in the same way as a :ref:`CompressorRMS`, but the
gain is then applied to the *input* sample.
This can be used to reduce the level of the *input* signal when the
*detect* signal gets above the threshold.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_compressor_rms_sidechain
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.drc.compressor_rms_sidechain_mono
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
.. _CompressorSidechainStereo:
-------------------------------
Stereo Sidechain RMS Compressor
-------------------------------
The stereo sidechain RMS compressor expands the :ref:`CompressorSidechain` to take 2
input and 2 detection channels. The envelope of each detection channel is taken, and
the maximum of the envelopes is used to calculate the compression to apply to the
input channels.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_compressor_rms_sidechain_stereo
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.drc.compressor_rms_sidechain_stereo
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
=========
Expanders
=========
An expander attenuates a signal when the envelope is below the threshold.
This increases the dynamic range of the signal, and can be used to
attenuate quiet signals, such as low level noise.
Like limiters and compressors, an expander will run an internal envelope
detector to calculate the envelope and compare it to the threshold.
If the envelope is below the threshold, the applied gain will be reduced.
If the envelope is greater than the threshold, unity gain will be applied.
The gain is run through an EMA to avoid abrupt changes.
The same `attack and release times`_ are used for the envelope detector
and the gain smoothing. In an expander, the attack time is defined as the
speed at which the gain returns to unity after the signal has been
below the threshold.
.. _NoiseGate:
----------
Noise Gate
----------
A noise gate uses the :ref:`EnvelopeDetectorPeak` to calculate the
envelope of the input signal.
When the envelope is below the threshold, a gain of 0 is applied to
the input signal. Otherwise, unity gain is applied.
.. doxygentypedef:: noise_gate_t
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_noise_gate
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.drc.noise_gate
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
.. _NoiseSuppressorExpander:
-------------------------
Noise Suppressor/Expander
-------------------------
A basic expander can also be used as a noise suppressor.
It uses the :ref:`EnvelopeDetectorPeak` to calculate the envelope of the
input signal.
When the envelope is below the threshold, the gain of the signal is
reduced according to the ratio. Otherwise, unity gain is applied.
Like a compressor, the ``ratio`` defines the input/output gradient in
the logarithmic domain.
For example, a ratio of 2 will reduce the output gain by 0.5 dB for every
1 dB the envelope is below the threshold.
A ratio of 1 will apply no gain changes.
To avoid converting the envelope to the logarithmic domain for the gain
calculation, the ratio is converted to the ``slope`` as
``(1 - ratio)``. The gain can then be calculated as an
exponential in the linear domain.
For speed, some parameters such as ``inv_threshold`` are computed at
initialisation to simplify run-time computation.
.. doxygenstruct:: noise_suppressor_expander_t
:members:
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_noise_suppressor_expander
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.drc.noise_suppressor_expander
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:

View File

@@ -0,0 +1,139 @@
.. _fir:
###############################
Finite Impulse Response Filters
###############################
Finite impulse response (FIR) filters allow the use of arbitrary filters
with a finite number of taps. This library does not provide FIR filter
design tools, but allows for coefficients to be imported from other design
tools, such as `SciPy/filter_design`_.
.. _FirDirect:
==========
FIR Direct
==========
The direct FIR implements the filter as a convolution in the time domain.
This library uses FIR ``filter_fir_s32`` implementation from ``lib_xcore_math`` to run on xcore.
More information on implementation can be found in `XCORE Math Library`_ documentation.
.. autoclass:: audio_dsp.dsp.fir.fir_direct
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
.. automethod:: check_coeff_scaling
:noindex:
.. _FirBlockTD:
=====================
Block Time Domain FIR
=====================
The block time domain FIR implements the filter as a convolution in the time domain, but with a block size
optimized for execution on the vector-unit of xcore.ai. The advantage with this one is it is over twice
the efficiency of the lib_xcore_math implementation. This block will generate C code for the block time domain FIR
filter.
More information on implementation can be found in
`AN02027: Efficient computation of FIR filters on the XCORE <https://www.xmos.com/application-notes/>`_.
.. note::
The block time domain FIR filter is not currently implemented as a DSP Stage, so cannot be
used with the DSP pipeline tool yet.
.. tab:: Autogenerator
.. only:: latex
.. rubric:: Autogenerator
.. autofunction:: audio_dsp.dsp.td_block_fir.generate_td_fir
:noindex:
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: td_block_fir_data_init
.. doxygenfunction:: td_block_fir_add_data
.. doxygenfunction:: td_block_fir_compute
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.td_block_fir.fir_block_td
:noindex:
.. automethod:: process_frame
:noindex:
.. automethod:: reset_state
:noindex:
.. _FirBlockFD:
==========================
Block Frequency Domain FIR
==========================
This implementation is a frequency-domain implementation resulting in a lower algorithmic
complexity than the time-domain versions. This will achieve the highest taps per second possible
with the xcore. The main cost to using this implementation is the memory requirements double
compared to the previous two time-domain versions. This block will generate C code for the block
frequency domain FIR filter.
More information on implementation can be found in
`AN02027: Efficient computation of FIR filters on the XCORE <https://www.xmos.com/application-notes/>`_.
.. note::
The block time domain FIR filter is not currently implemented as a DSP Stage, so cannot be
used with the DSP pipeline tool yet.
.. tab:: Autogenerator
.. only:: latex
.. rubric:: Autogenerator
.. autofunction:: audio_dsp.dsp.fd_block_fir.generate_fd_fir
:noindex:
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: fd_block_fir_data_init
.. doxygenfunction:: fd_block_fir_add_data
.. doxygenfunction:: fd_block_fir_compute
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.fd_block_fir.fir_block_fd
:noindex:
.. automethod:: process_frame
:noindex:
.. automethod:: reset_state
:noindex:

View File

@@ -0,0 +1,45 @@
.. _geq:
==================
Graphic Equalisers
==================
.. _GraphicEq10b:
=========================
10 Band Graphic Equaliser
=========================
The graphic EQ module creates a 10 band equaliser, with octave spaced
center frequencies. This can be used to
The equaliser is implemented as a set of parallel 4th order bandpass
filters, with a gain controlling the level of each parallel branch.
The center frequencies are:
[32, 64, 125, 250, 500, 1000, 2000, 4000, 8000, 16000].
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_graphic_eq_10b
See also :c:func:`adsp_graphic_eq_10b_init`.
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.graphic_eq.graphic_eq_10_band
:noindex:
.. automethod:: process
:noindex:
.. autoproperty:: gains_db
:noindex:

View File

@@ -0,0 +1,30 @@
.. raw:: latex
\newpage
.. _dsp_modules_section:
DSP Modules
===========
In lib_audio_dsp, DSP modules are the lower level functions and APIs.
These can be used directly without the pipeline building tool. The
documentation also includes more implementation details about the DSP
algorithms. It includes topics such as C and Python APIs,
providing more detailed view of the DSP modules.
Each DSP module has been implemented in floating point Python, fixed
point int32 Python and fixed point int32 C, with optimisations for xcore.
The Python and C fixed point implementations aim to be bit exact with
each other, allowing for Python prototyping of DSP pipelines.
.. toctree::
:maxdepth: 1
biquads.rst
drc.rst
fir.rst
graphic_eq.rst
reverb.rst
signal_chain.rst
more_python.rst

View File

@@ -0,0 +1,43 @@
########################
Python module base class
########################
All the Python DSP modules are based on a common base class.
In order to keep the documentation short, all Python classes in the
previous sections only had the ``process`` method described, and control
methods where necessary.
This section provides the user with a more in-depth information of the
Python API, which may be useful when adding custom DSP modules.
Some classes overload the base class APIs where they require different
input data types or dimensions. However, they will all have the
attributes and methods described below.
The process methods can be split into 2 groups:
1) ``process`` is a 64b floating point implementation
2) ``process_xcore`` is a 32b fixed-point implementation, with the aim of
being bit exact with the C/assembly implementation.
The ``process_xcore`` methods can be used to simulate the xcore
implementation precision and the noise floor. The Python ``process_xcore``
implementations have very similar accuracy to the xcore
C ``adsp_*`` implementations (subject to the module and implementation).
Python simulation methods tend to be slower as Python has a limited support
for the fixed point processing. Bit exactness is not always possible
for modules that use 32b float operations, as the rounding of these can
differ between C libraries.
There are 3 layers of ``process`` functions:
1) ``process`` operates for a single sample on a single channel
2) ``process_channels`` operates on all channels for a single sample
3) ``process_frame`` operates on all samples and channels for a single frame.
A DSP module may overload one or all of these, depending if it operates
sample-wise, channel-wise or frame-wise. It is expected that
``process_frame`` will be called by higher level DSP Stages.
.. autoclass:: audio_dsp.dsp.generic.dsp_block
:members:
:noindex:

View File

@@ -0,0 +1,173 @@
.. _reverb:
######
Reverb
######
.. _ReverbRoom:
===========
Reverb Room
===========
The room reverb module imitates the reflections of a room. The algorithm is a
Schroeder style reverberation, based on `Freeverb by Jezar at Dreampoint <https://www.dsprelated.com/freebooks/pasp/Freeverb.html>`_.
It consists of the wet predelay, 8 parallel comb filters fed into 4 series all-pass filters,
with a wet and dry microphone control to set the effect level.
For more details on the algorithm, see `Physical Audio Signal Processing
<https://www.dsprelated.com/freebooks/pasp/Freeverb.html>`_ by Julius Smith.
.. doxygenstruct:: reverb_room_t
:members:
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_reverb_room
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.reverb.reverb_room
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
.. automethod:: set_wet_dry_mix
:noindex:
.. automethod:: set_pre_gain
:noindex:
.. automethod:: set_wet_gain
:noindex:
.. automethod:: set_dry_gain
:noindex:
.. automethod:: set_decay
:noindex:
.. automethod:: set_damping
:noindex:
.. automethod:: set_room_size
:noindex:
.. _ReverbRoomStereo:
==================
Reverb Room Stereo
==================
The stereo room reverb module extends the mono :ref:`ReverbRoom` by adding a second
set of comb and all-pass filters in parallel, and mixing the output of the
two networks. Varying the mix of the networks changes the stereo width of
the effect.
For more details on the algorithm, see `Physical Audio Signal Processing
<https://www.dsprelated.com/freebooks/pasp/Freeverb.html>`_ by Julius Smith.
.. doxygenstruct:: reverb_room_st_t
:members:
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_reverb_room_st
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.reverb_stereo.reverb_room_stereo
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
.. automethod:: set_wet_dry_mix
:noindex:
.. autoproperty:: wet_db
:noindex:
.. autoproperty:: dry_db
:noindex:
.. autoproperty:: decay
:noindex:
.. autoproperty:: damping
:noindex:
.. _ReverbPlateStereo:
===================
Reverb Plate Stereo
===================
The plate reverb module imitates the reflections of a plate reverb,
which has more early reflections than the room reverb. The algorithm is
based on Dattorro's 1997 paper. This reverb consists of 4 allpass
filters for input diffusion, followed by a figure of 8 reverb tank of
allpasses, low-pass filters, and delays. The output is taken from
multiple taps in the delay lines to get a desirable echo density.
The left and right output can be mixed with various widths.
For more details on the algorithm, see
`Effect Design, Part 1: Reverberator and Other Filters
<https://aes2.org/publications/elibrary-page/?id=10160>`_ by Jon Dattorro.
.. doxygenstruct:: reverb_plate_t
:members:
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_reverb_plate
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.reverb_plate.reverb_plate_stereo
:noindex:
.. automethod:: process
:noindex:
.. automethod:: reset_state
:noindex:
.. automethod:: set_wet_dry_mix
:noindex:

View File

@@ -0,0 +1,332 @@
.. _signal_chain:
#######################
Signal Chain Components
#######################
Signal chain components includes DSP modules for:
* combining signals, such as subtracting, adding, and mixing
* forks for splitting signals
* basic gain components, such as fixed gain, volume control, and mute
* basic delay buffers.
.. _Adder:
=====
Adder
=====
The adder will add samples from N inputs together.
It will round and saturate the result to the Q0.31 range.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_adder
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.signal_chain.adder
:noindex:
.. automethod:: process_channels
:noindex:
.. _Subtractor:
==========
Subtractor
==========
The subtractor will subtract one sample from another, then round and saturate the difference to Q0.31 range.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_subtractor
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.signal_chain.subtractor
:noindex:
.. automethod:: process_channels
:noindex:
.. _FixedGain:
==========
Fixed Gain
==========
This module applies a fixed gain to a sample, with rounding and saturation to Q0.31 range.
The gain must be in ``Q_GAIN`` format.
.. doxygendefine:: Q_GAIN
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_fixed_gain
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.signal_chain.fixed_gain
:noindex:
.. automethod:: process
:noindex:
.. _Mixer:
=====
Mixer
=====
The mixer applies a gain to all N channels of input samples and adds them together.
The sum is rounded and saturated to Q0.31 range. The gain must be in ``Q_GAIN`` format.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_mixer
An alternative way to implement a mixer is to multiply-accumulate the
input samples into a 64-bit word, then saturate it to a 32-bit word using:
.. doxygenfunction:: adsp_saturate_32b
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.signal_chain.mixer
:noindex:
.. automethod:: process_channels
:noindex:
.. _VolumeControl:
==============
Volume Control
==============
The volume control allows safe real-time gain adjustments with minimal
artifacts.
When the target gain is changed, a slew is used to move from the
current gain to the target gain.
This allows smooth gain change and no clicks in the output signal.
The mute API allows the user to safely mute the signal by setting the
target gain to ``0``, with the slew ensuring no pops or clicks.
Unmuting will restore the pre-mute target gain.
The new gain can be set while muted, but will not take effect until
unmute is called.
There are separate APIs for process, setting the gain, muting and
unmuting so that volume control can easily be implemented into the
control system.
The slew is applied as an exponential of the difference between the
target and current gain.
For run-time efficiency, instead of an EMA-style alpha, the difference
is right shifted by the ``slew_shift`` parameter. The relation between
``slew_shift`` and time is further discussed in the Python class
documentation.
.. doxygenstruct:: volume_control_t
:members:
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_volume_control
.. doxygenfunction:: adsp_volume_control_set_gain
.. doxygenfunction:: adsp_volume_control_mute
.. doxygenfunction:: adsp_volume_control_unmute
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.signal_chain.volume_control
:noindex:
.. automethod:: process
:noindex:
.. automethod:: set_gain
:noindex:
.. automethod:: mute
:noindex:
.. automethod:: unmute
:noindex:
.. _Delay:
=====
Delay
=====
The delay module uses a memory buffer to return a sample after a specified
time period.
The returned samples will be delayed by a specified value.
The ``max_delay`` is set at initialisation, and sets the amount of
memory used by the buffers. It cannot be changed at runtime.
The current ``delay`` value can be changed at runtime within the range
``[0, max_delay]``
.. doxygenstruct:: delay_t
:members:
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_delay
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.signal_chain.delay
:noindex:
.. automethod:: process_channels
:noindex:
.. automethod:: reset_state
:noindex:
.. automethod:: set_delay
:noindex:
.. _SwitchSlew:
================
Switch with slew
================
The slewing switch module uses a cosine crossfade when moving switch
position in order to avoid clicks.
.. doxygenstruct:: switch_slew_t
:members:
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_switch_slew
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.signal_chain.switch_slew
:noindex:
.. automethod:: process_channels
:noindex:
.. automethod:: move_switch
:noindex:
.. _Crossfader:
==========
Crossfader
==========
The crossfader mixes between two sets of inputs.
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_crossfader
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
Only a slewing crossfader Python API is provided.
====================
Crossfader with slew
====================
The crossfader mixes between two sets of inputs, with slew applied to the
gains when they are changed.
.. doxygenstruct:: crossfader_slew_t
:members:
.. tab:: C API
.. only:: latex
.. rubric:: C API
.. doxygenfunction:: adsp_crossfader_slew
.. tab:: Python API
.. only:: latex
.. rubric:: Python API
.. autoclass:: audio_dsp.dsp.signal_chain.crossfader
:noindex:
.. automethod:: process_channels
:noindex:

View File

@@ -0,0 +1,15 @@
.. _pipeline_design_api:
Pipeline Design API
###################
This page describes the C and Python APIs that will be needed when using the pipeline design utility.
When designing a pipeline first create an instance of ``Pipeline``, add threads to it with ``Pipeline.add_thread()``. Then add DSP stages such as ``Biquad`` using ``CompositeStage.stage()``.
The pipeline can be visualised in a `Jupyter Notebook`_ using ``Pipeline.draw()`` and the xcore source code for the pipeline can be generated using ``generate_dsp_main()``.
.. contents::
:local:
:class: this-will-duplicate-information-and-it-is-still-useful-here
.. include:: gen/audio_dsp.design.inc

View File

@@ -0,0 +1,27 @@
.. _dsp_stages_section:
DSP Stages
==========
DSP stages are high level blocks for use in the Python DSP
pipeline tool. Each Stage has a Python and C implementation, allowing
pipelines to be rapidly prototyped in Python before being easily
deployed to hardware in C. The audio performance of both implementations
is equivalent.
Most stages have parameters that can be changed at runtime, and the
available parameters are outlined in the documentation.
All the DSP stages can be imported into a Python file using:
.. code-block:: console
from audio_dsp.stages import *
The following DSP stages are available for use in the Python DSP pipeline design.
.. toctree::
:glob:
:maxdepth: 2
gen/*

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -0,0 +1,238 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 12.2.1 (20241206.2353)
-->
<!-- Pages: 1 -->
<svg width="337pt" height="555pt"
viewBox="0.00 0.00 337.11 555.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 551)">
<polygon fill="white" stroke="none" points="-4,4 -4,-551 333.11,-551 333.11,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster_8eb9872f161a4ef5b708a32f431a0abf</title>
<polygon fill="none" stroke="grey" points="8,-114 8,-457.5 312,-457.5 312,-114 8,-114"/>
<text text-anchor="middle" x="160" y="-440.2" font-family="Times New Roman,serif" font-size="14.00" fill="grey">Thread 0</text>
</g>
<!-- d8f967fda0cb48cd9cb6ef83bb81905d -->
<g id="node1" class="node">
<title>d8f967fda0cb48cd9cb6ef83bb81905d</title>
<polygon fill="none" stroke="black" points="16.25,-310 16.25,-424.5 99.75,-424.5 99.75,-310 16.25,-310"/>
<text text-anchor="middle" x="37.12" y="-407.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="58,-400 58,-424.5"/>
<text text-anchor="middle" x="78.88" y="-407.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="16.25,-400 99.75,-400"/>
<text text-anchor="middle" x="58" y="-382.7" font-family="Times New Roman,serif" font-size="14.00">2: Biquad_0</text>
<polyline fill="none" stroke="black" points="16.25,-375.5 99.75,-375.5"/>
<text text-anchor="middle" x="58" y="-358.2" font-family="Times New Roman,serif" font-size="14.00">filter_type: </text>
<text text-anchor="middle" x="58" y="-341.7" font-family="Times New Roman,serif" font-size="14.00">type: bypass</text>
<polyline fill="none" stroke="black" points="16.25,-334.5 99.75,-334.5"/>
<text text-anchor="middle" x="37.12" y="-317.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="58,-310 58,-334.5"/>
<text text-anchor="middle" x="78.88" y="-317.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- bcaaad1b2e28442a8871cf4a3415deef -->
<g id="node4" class="node">
<title>bcaaad1b2e28442a8871cf4a3415deef</title>
<polygon fill="none" stroke="black" points="112.25,-122.5 112.25,-237 195.75,-237 195.75,-122.5 112.25,-122.5"/>
<text text-anchor="middle" x="122.62" y="-219.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="133,-212.5 133,-237"/>
<text text-anchor="middle" x="143.38" y="-219.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="153.75,-212.5 153.75,-237"/>
<text text-anchor="middle" x="164.12" y="-219.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="174.5,-212.5 174.5,-237"/>
<text text-anchor="middle" x="184.88" y="-219.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="112.25,-212.5 195.75,-212.5"/>
<text text-anchor="middle" x="154" y="-195.2" font-family="Times New Roman,serif" font-size="14.00">5: Biquad_3</text>
<polyline fill="none" stroke="black" points="112.25,-188 195.75,-188"/>
<text text-anchor="middle" x="154" y="-170.7" font-family="Times New Roman,serif" font-size="14.00">filter_type: </text>
<text text-anchor="middle" x="154" y="-154.2" font-family="Times New Roman,serif" font-size="14.00">type: bypass</text>
<polyline fill="none" stroke="black" points="112.25,-147 195.75,-147"/>
<text text-anchor="middle" x="122.62" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="133,-122.5 133,-147"/>
<text text-anchor="middle" x="143.38" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="153.75,-122.5 153.75,-147"/>
<text text-anchor="middle" x="164.12" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="174.5,-122.5 174.5,-147"/>
<text text-anchor="middle" x="184.88" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- d8f967fda0cb48cd9cb6ef83bb81905d&#45;&gt;bcaaad1b2e28442a8871cf4a3415deef -->
<g id="edge8" class="edge">
<title>d8f967fda0cb48cd9cb6ef83bb81905d:s&#45;&gt;bcaaad1b2e28442a8871cf4a3415deef:n</title>
<path fill="none" stroke="black" d="M37.12,-309.5C37.12,-264.09 108.56,-283.05 120.84,-248.7"/>
<polygon fill="black" stroke="black" points="124.27,-249.42 122.39,-238.99 117.36,-248.32 124.27,-249.42"/>
</g>
<!-- d8f967fda0cb48cd9cb6ef83bb81905d&#45;&gt;bcaaad1b2e28442a8871cf4a3415deef -->
<g id="edge9" class="edge">
<title>d8f967fda0cb48cd9cb6ef83bb81905d:s&#45;&gt;bcaaad1b2e28442a8871cf4a3415deef:n</title>
<path fill="none" stroke="black" d="M78.88,-309.5C78.88,-270.73 131.39,-278.29 141.65,-248.57"/>
<polygon fill="black" stroke="black" points="145.06,-249.42 143.14,-239 138.14,-248.34 145.06,-249.42"/>
</g>
<!-- 5f8f2b330b404524baca9bd51037ff9f -->
<g id="node2" class="node">
<title>5f8f2b330b404524baca9bd51037ff9f</title>
<polygon fill="none" stroke="black" points="118.25,-310 118.25,-424.5 201.75,-424.5 201.75,-310 118.25,-310"/>
<text text-anchor="middle" x="159.62" y="-407.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="118.25,-400 201.75,-400"/>
<text text-anchor="middle" x="160" y="-382.7" font-family="Times New Roman,serif" font-size="14.00">3: Biquad_1</text>
<polyline fill="none" stroke="black" points="118.25,-375.5 201.75,-375.5"/>
<text text-anchor="middle" x="160" y="-358.2" font-family="Times New Roman,serif" font-size="14.00">filter_type: </text>
<text text-anchor="middle" x="160" y="-341.7" font-family="Times New Roman,serif" font-size="14.00">type: bypass</text>
<polyline fill="none" stroke="black" points="118.25,-334.5 201.75,-334.5"/>
<text text-anchor="middle" x="159.62" y="-317.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- 5f8f2b330b404524baca9bd51037ff9f&#45;&gt;bcaaad1b2e28442a8871cf4a3415deef -->
<g id="edge10" class="edge">
<title>5f8f2b330b404524baca9bd51037ff9f:s&#45;&gt;bcaaad1b2e28442a8871cf4a3415deef:n</title>
<path fill="none" stroke="black" d="M159.62,-309.5C159.62,-281.57 163.04,-271.99 163.92,-248.8"/>
<polygon fill="black" stroke="black" points="167.42,-249.08 164.1,-239.01 160.42,-248.95 167.42,-249.08"/>
</g>
<!-- 68bea47647ed4903993da32df86e4fb9 -->
<g id="node3" class="node">
<title>68bea47647ed4903993da32df86e4fb9</title>
<polygon fill="none" stroke="black" points="220.25,-310 220.25,-424.5 303.75,-424.5 303.75,-310 220.25,-310"/>
<text text-anchor="middle" x="234.12" y="-407.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="248,-400 248,-424.5"/>
<text text-anchor="middle" x="261.88" y="-407.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="275.75,-400 275.75,-424.5"/>
<text text-anchor="middle" x="289.62" y="-407.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="220.25,-400 303.75,-400"/>
<text text-anchor="middle" x="262" y="-382.7" font-family="Times New Roman,serif" font-size="14.00">4: Biquad_2</text>
<polyline fill="none" stroke="black" points="220.25,-375.5 303.75,-375.5"/>
<text text-anchor="middle" x="262" y="-358.2" font-family="Times New Roman,serif" font-size="14.00">filter_type: </text>
<text text-anchor="middle" x="262" y="-341.7" font-family="Times New Roman,serif" font-size="14.00">type: bypass</text>
<polyline fill="none" stroke="black" points="220.25,-334.5 303.75,-334.5"/>
<text text-anchor="middle" x="234.12" y="-317.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="248,-310 248,-334.5"/>
<text text-anchor="middle" x="261.88" y="-317.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="275.75,-310 275.75,-334.5"/>
<text text-anchor="middle" x="289.62" y="-317.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- 68bea47647ed4903993da32df86e4fb9&#45;&gt;bcaaad1b2e28442a8871cf4a3415deef -->
<g id="edge11" class="edge">
<title>68bea47647ed4903993da32df86e4fb9:s&#45;&gt;bcaaad1b2e28442a8871cf4a3415deef:n</title>
<path fill="none" stroke="black" d="M234.12,-309.5C234.12,-274.97 195.06,-275.59 186.51,-248.75"/>
<polygon fill="black" stroke="black" points="190,-248.39 185.09,-239 183.07,-249.4 190,-248.39"/>
</g>
<!-- end -->
<g id="node6" class="node">
<title>end</title>
<polygon fill="none" stroke="black" points="109.75,-0.5 109.75,-49.5 246.25,-49.5 246.25,-0.5 109.75,-0.5"/>
<text text-anchor="middle" x="121.12" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="132.5,-25 132.5,-49.5"/>
<text text-anchor="middle" x="143.88" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">1</text>
<polyline fill="none" stroke="black" points="155.25,-25 155.25,-49.5"/>
<text text-anchor="middle" x="166.62" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">2</text>
<polyline fill="none" stroke="black" points="178,-25 178,-49.5"/>
<text text-anchor="middle" x="189.38" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">3</text>
<polyline fill="none" stroke="black" points="200.75,-25 200.75,-49.5"/>
<text text-anchor="middle" x="212.12" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">4</text>
<polyline fill="none" stroke="black" points="223.5,-25 223.5,-49.5"/>
<text text-anchor="middle" x="234.88" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">5</text>
<polyline fill="none" stroke="black" points="109.75,-25 246.25,-25"/>
<text text-anchor="middle" x="178" y="-7.7" font-family="Times New Roman,serif" font-size="14.00">end</text>
</g>
<!-- 68bea47647ed4903993da32df86e4fb9&#45;&gt;end -->
<g id="edge12" class="edge">
<title>68bea47647ed4903993da32df86e4fb9:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M261.88,-309.5C261.88,-195.97 215.37,-169.17 212.29,-61.4"/>
<polygon fill="black" stroke="black" points="215.79,-61.46 212.15,-51.51 208.79,-61.56 215.79,-61.46"/>
</g>
<!-- 68bea47647ed4903993da32df86e4fb9&#45;&gt;end -->
<g id="edge13" class="edge">
<title>68bea47647ed4903993da32df86e4fb9:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M289.62,-309.5C289.62,-220.84 269,-200.6 250,-114 244.64,-89.58 237.38,-81.49 235.4,-61.37"/>
<polygon fill="black" stroke="black" points="238.9,-61.34 234.94,-51.51 231.91,-61.66 238.9,-61.34"/>
</g>
<!-- bcaaad1b2e28442a8871cf4a3415deef&#45;&gt;end -->
<g id="edge14" class="edge">
<title>bcaaad1b2e28442a8871cf4a3415deef:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M122.62,-121.5C122.62,-93.81 121.49,-84.23 121.19,-61.2"/>
<polygon fill="black" stroke="black" points="124.7,-61.49 121.13,-51.51 117.7,-61.53 124.7,-61.49"/>
</g>
<!-- bcaaad1b2e28442a8871cf4a3415deef&#45;&gt;end -->
<g id="edge15" class="edge">
<title>bcaaad1b2e28442a8871cf4a3415deef:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M143.38,-121.5C143.38,-93.94 143.75,-84.31 143.85,-61.51"/>
<polygon fill="black" stroke="black" points="147.35,-61.52 143.87,-51.51 140.35,-61.51 147.35,-61.52"/>
</g>
<!-- bcaaad1b2e28442a8871cf4a3415deef&#45;&gt;end -->
<g id="edge16" class="edge">
<title>bcaaad1b2e28442a8871cf4a3415deef:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M164.12,-121.5C164.12,-93.8 166.02,-84.23 166.51,-61.21"/>
<polygon fill="black" stroke="black" points="170.01,-61.55 166.61,-51.51 163.01,-61.48 170.01,-61.55"/>
</g>
<!-- bcaaad1b2e28442a8871cf4a3415deef&#45;&gt;end -->
<g id="edge17" class="edge">
<title>bcaaad1b2e28442a8871cf4a3415deef:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M184.88,-121.5C184.88,-93.76 188.29,-84.26 189.17,-61.22"/>
<polygon fill="black" stroke="black" points="192.66,-61.58 189.35,-51.51 185.67,-61.45 192.66,-61.58"/>
</g>
<!-- start -->
<g id="node5" class="node">
<title>start</title>
<polygon fill="none" stroke="black" points="137.38,-497.5 137.38,-546.5 296.62,-546.5 296.62,-497.5 137.38,-497.5"/>
<text text-anchor="middle" x="217" y="-529.2" font-family="Times New Roman,serif" font-size="14.00">start</text>
<polyline fill="none" stroke="black" points="137.38,-522 296.62,-522"/>
<text text-anchor="middle" x="148.75" y="-504.7" font-family="Times New Roman,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="160.12,-497.5 160.12,-522"/>
<text text-anchor="middle" x="171.5" y="-504.7" font-family="Times New Roman,serif" font-size="14.00">1</text>
<polyline fill="none" stroke="black" points="182.88,-497.5 182.88,-522"/>
<text text-anchor="middle" x="194.25" y="-504.7" font-family="Times New Roman,serif" font-size="14.00">2</text>
<polyline fill="none" stroke="black" points="205.62,-497.5 205.62,-522"/>
<text text-anchor="middle" x="217" y="-504.7" font-family="Times New Roman,serif" font-size="14.00">3</text>
<polyline fill="none" stroke="black" points="228.38,-497.5 228.38,-522"/>
<text text-anchor="middle" x="239.75" y="-504.7" font-family="Times New Roman,serif" font-size="14.00">4</text>
<polyline fill="none" stroke="black" points="251.12,-497.5 251.12,-522"/>
<text text-anchor="middle" x="262.5" y="-504.7" font-family="Times New Roman,serif" font-size="14.00">5</text>
<polyline fill="none" stroke="black" points="273.88,-497.5 273.88,-522"/>
<text text-anchor="middle" x="285.25" y="-504.7" font-family="Times New Roman,serif" font-size="14.00">6</text>
</g>
<!-- start&#45;&gt;d8f967fda0cb48cd9cb6ef83bb81905d -->
<g id="edge1" class="edge">
<title>start:s&#45;&gt;d8f967fda0cb48cd9cb6ef83bb81905d:n</title>
<path fill="none" stroke="black" d="M148.75,-497C148.75,-442.34 52.68,-478.27 38.79,-436.7"/>
<polygon fill="black" stroke="black" points="42.28,-436.37 37.35,-427 35.36,-437.4 42.28,-436.37"/>
</g>
<!-- start&#45;&gt;d8f967fda0cb48cd9cb6ef83bb81905d -->
<g id="edge2" class="edge">
<title>start:s&#45;&gt;d8f967fda0cb48cd9cb6ef83bb81905d:n</title>
<path fill="none" stroke="black" d="M171.5,-497C171.5,-449.26 93.45,-472.74 80.64,-436.69"/>
<polygon fill="black" stroke="black" points="84.13,-436.33 79.11,-426.99 77.22,-437.42 84.13,-436.33"/>
</g>
<!-- start&#45;&gt;5f8f2b330b404524baca9bd51037ff9f -->
<g id="edge3" class="edge">
<title>start:s&#45;&gt;5f8f2b330b404524baca9bd51037ff9f:n</title>
<path fill="none" stroke="black" d="M194.25,-497C194.25,-465.83 167.26,-461.49 160.94,-436.61"/>
<polygon fill="black" stroke="black" points="164.45,-436.52 159.8,-427 157.5,-437.35 164.45,-436.52"/>
</g>
<!-- start&#45;&gt;68bea47647ed4903993da32df86e4fb9 -->
<g id="edge4" class="edge">
<title>start:s&#45;&gt;68bea47647ed4903993da32df86e4fb9:n</title>
<path fill="none" stroke="black" d="M217,-497C217,-468.54 229.99,-460.2 233.34,-436.93"/>
<polygon fill="black" stroke="black" points="236.83,-437.23 234.02,-427.01 229.85,-436.75 236.83,-437.23"/>
</g>
<!-- start&#45;&gt;68bea47647ed4903993da32df86e4fb9 -->
<g id="edge6" class="edge">
<title>start:s&#45;&gt;68bea47647ed4903993da32df86e4fb9:n</title>
<path fill="none" stroke="black" d="M262.5,-497C262.5,-469.44 262.03,-459.81 261.91,-437.01"/>
<polygon fill="black" stroke="black" points="265.41,-437 261.88,-427.01 258.41,-437.02 265.41,-437"/>
</g>
<!-- start&#45;&gt;68bea47647ed4903993da32df86e4fb9 -->
<g id="edge7" class="edge">
<title>start:s&#45;&gt;68bea47647ed4903993da32df86e4fb9:n</title>
<path fill="none" stroke="black" d="M285.25,-497C285.25,-469.27 288.57,-459.75 289.43,-436.72"/>
<polygon fill="black" stroke="black" points="292.92,-437.07 289.6,-427.01 285.92,-436.95 292.92,-437.07"/>
</g>
<!-- 2f81a928f2334884a59fa71a54216bb9 -->
<g id="node7" class="node">
<title>2f81a928f2334884a59fa71a54216bb9</title>
<ellipse fill="black" stroke="black" cx="324" cy="-367.25" rx="1.8" ry="1.8"/>
</g>
<!-- start&#45;&gt;2f81a928f2334884a59fa71a54216bb9 -->
<g id="edge5" class="edge">
<title>start:s&#45;&gt;2f81a928f2334884a59fa71a54216bb9</title>
<path fill="none" stroke="black" d="M239.75,-497C239.75,-458.83 294.73,-489.19 316,-457.5 331.74,-434.04 329.18,-398.95 326.35,-380.54"/>
<polygon fill="black" stroke="black" points="329.8,-379.91 324.51,-370.73 322.92,-381.2 329.8,-379.91"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,115 @@
# Copyright 2024-2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
"""Generate complex pipeline diagrams for documentation."""
from audio_dsp.design.pipeline import Pipeline
from audio_dsp.stages import Biquad, Fork, Bypass
# NOTE: The :width: in the .rst is set to {.png_width}/100 for consistent scaling,
# but the svg images are used
if __name__ == "__main__":
file_ext = "png"
# start with 7 input channels
p, inputs = Pipeline.begin(7, fs=48000)
# pass the first 2 inputs to a 2-channel Biquad
i0 = p.stage(Biquad, inputs[0:2])
# pass the third input (index 2) to a 1-channel biquad
i1 = p.stage(Biquad, inputs[2])
# pass the inputs at index 3, 5, and 6 to a 3 channel biquad
i2 = p.stage(Biquad, inputs[3, 5, 6])
# pass all of i0 and i1, as well as the first channel in i2
# to create a 4 channel biquad
i3 = p.stage(Biquad, i0 + i1 + i2[0])
# The pipeline output has 6 channels:
# - all four i3 channels
# - the 2nd and 3rd channel from i2
p.set_outputs(i3 + i2[1:])
p.draw(f"doc/rst/images/complex_pipelines/7_chan_biquad_pipeline.{file_ext}")
#%%
p, inputs = Pipeline.begin(1, fs=48000)
# fork the input to create a 2 channel signal
x = p.stage(Fork, inputs, count=2)
# fork again to create a 4 channel signal
x = p.stage(Fork, x, count=2)
# there are now 4 channels in the pipeline output
p.set_outputs(x)
p.draw(f"doc/rst/images/complex_pipelines/fork_pipeline.{file_ext}")
#%%
p, i = Pipeline.begin(1, fs=48000)
# thread 0
i = p.stage(Biquad, i)
# thread 1
p.next_thread()
i = p.stage(Biquad, i)
# thread 2
p.next_thread()
i = p.stage(Biquad, i)
p.set_outputs(i)
p.draw(f"doc/rst/images/complex_pipelines/multi_thread_pipeline.{file_ext}")
#%%
p, inputs = Pipeline.begin(2, fs=48000)
# inputs[1] is not used on thread 0
x1 = p.stage(Biquad, inputs[0])
p.next_thread()
# inputs[1] first used on thread 1
x = p.stage(Biquad, x1 + inputs[1])
p.set_outputs(x)
p.draw(f"doc/rst/images/complex_pipelines/thread_crossings_bad.{file_ext}")
#%%
p, inputs = Pipeline.begin(2, fs=48000)
# both inputs are not used on this thread
x1 = p.stage(Biquad, inputs[0])
x2 = p.stage(Bypass, inputs[1])
p.next_thread()
x = p.stage(Biquad, x1 + x2)
p.set_outputs(x)
p.draw(f"doc/rst/images/complex_pipelines/thread_crossings_bypass.{file_ext}")
#%%
p, inputs = Pipeline.begin(2, fs=48000)
x1 = p.stage(Biquad, inputs[0])
p.next_thread()
x2 = p.stage(Bypass, inputs[1])
p.next_thread()
# x1 and x2 have both crossed 1 thread already
x = p.stage(Biquad, x1 + x2)
p.set_outputs(x)
p.draw(f"doc/rst/images/complex_pipelines/thread_crossings_parallel.{file_ext}")

View File

@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 12.2.1 (20241206.2353)
-->
<!-- Pages: 1 -->
<svg width="120pt" height="473pt"
viewBox="0.00 0.00 120.00 473.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 469)">
<polygon fill="white" stroke="none" points="-4,4 -4,-469 116,-469 116,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster_34060b0cf6eb49cb88a4f6401e52f7bd</title>
<polygon fill="none" stroke="grey" points="8,-114 8,-375.5 104,-375.5 104,-114 8,-114"/>
<text text-anchor="middle" x="56" y="-358.2" font-family="Times New Roman,serif" font-size="14.00" fill="grey">Thread 0</text>
</g>
<!-- 332548901fbe4af38afefb4d353e362b -->
<g id="node1" class="node">
<title>332548901fbe4af38afefb4d353e362b</title>
<polygon fill="none" stroke="black" points="21.38,-269 21.38,-342.5 90.62,-342.5 90.62,-269 21.38,-269"/>
<text text-anchor="middle" x="55.75" y="-325.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="21.38,-318 90.62,-318"/>
<text text-anchor="middle" x="56" y="-300.7" font-family="Times New Roman,serif" font-size="14.00">2: Fork_0</text>
<polyline fill="none" stroke="black" points="21.38,-293.5 90.62,-293.5"/>
<text text-anchor="middle" x="38.25" y="-276.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="55.12,-269 55.12,-293.5"/>
<text text-anchor="middle" x="72.5" y="-276.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- 59d14377fd1646b58c1d7744ff99b3eb -->
<g id="node2" class="node">
<title>59d14377fd1646b58c1d7744ff99b3eb</title>
<polygon fill="none" stroke="black" points="16.5,-122.5 16.5,-196 95.5,-196 95.5,-122.5 16.5,-122.5"/>
<text text-anchor="middle" x="35.88" y="-178.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="55.25,-171.5 55.25,-196"/>
<text text-anchor="middle" x="75.12" y="-178.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="16.5,-171.5 95.5,-171.5"/>
<text text-anchor="middle" x="56" y="-154.2" font-family="Times New Roman,serif" font-size="14.00">3: Fork_1</text>
<polyline fill="none" stroke="black" points="16.5,-147 95.5,-147"/>
<text text-anchor="middle" x="26.38" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="36.25,-122.5 36.25,-147"/>
<text text-anchor="middle" x="46.12" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="56,-122.5 56,-147"/>
<text text-anchor="middle" x="65.88" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="75.75,-122.5 75.75,-147"/>
<text text-anchor="middle" x="85.62" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- 332548901fbe4af38afefb4d353e362b&#45;&gt;59d14377fd1646b58c1d7744ff99b3eb -->
<g id="edge2" class="edge">
<title>332548901fbe4af38afefb4d353e362b:s&#45;&gt;59d14377fd1646b58c1d7744ff99b3eb:n</title>
<path fill="none" stroke="black" d="M38.25,-268.5C38.25,-240.61 36.45,-230.97 35.98,-207.79"/>
<polygon fill="black" stroke="black" points="39.49,-207.98 35.89,-198.01 32.49,-208.05 39.49,-207.98"/>
</g>
<!-- 332548901fbe4af38afefb4d353e362b&#45;&gt;59d14377fd1646b58c1d7744ff99b3eb -->
<g id="edge3" class="edge">
<title>332548901fbe4af38afefb4d353e362b:s&#45;&gt;59d14377fd1646b58c1d7744ff99b3eb:n</title>
<path fill="none" stroke="black" d="M72.5,-268.5C72.5,-240.61 74.49,-230.97 75.01,-207.79"/>
<polygon fill="black" stroke="black" points="78.5,-208.05 75.11,-198.01 71.5,-207.98 78.5,-208.05"/>
</g>
<!-- end -->
<g id="node4" class="node">
<title>end</title>
<polygon fill="none" stroke="black" points="10.5,-0.5 10.5,-49.5 101.5,-49.5 101.5,-0.5 10.5,-0.5"/>
<text text-anchor="middle" x="21.88" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="33.25,-25 33.25,-49.5"/>
<text text-anchor="middle" x="44.62" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">1</text>
<polyline fill="none" stroke="black" points="56,-25 56,-49.5"/>
<text text-anchor="middle" x="67.38" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">2</text>
<polyline fill="none" stroke="black" points="78.75,-25 78.75,-49.5"/>
<text text-anchor="middle" x="90.12" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">3</text>
<polyline fill="none" stroke="black" points="10.5,-25 101.5,-25"/>
<text text-anchor="middle" x="56" y="-7.7" font-family="Times New Roman,serif" font-size="14.00">end</text>
</g>
<!-- 59d14377fd1646b58c1d7744ff99b3eb&#45;&gt;end -->
<g id="edge4" class="edge">
<title>59d14377fd1646b58c1d7744ff99b3eb:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M26.38,-121.5C26.38,-93.76 22.96,-84.26 22.08,-61.22"/>
<polygon fill="black" stroke="black" points="25.58,-61.45 21.9,-51.51 18.59,-61.58 25.58,-61.45"/>
</g>
<!-- 59d14377fd1646b58c1d7744ff99b3eb&#45;&gt;end -->
<g id="edge5" class="edge">
<title>59d14377fd1646b58c1d7744ff99b3eb:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M46.12,-121.5C46.12,-93.81 44.99,-84.23 44.69,-61.2"/>
<polygon fill="black" stroke="black" points="48.2,-61.49 44.63,-51.51 41.2,-61.53 48.2,-61.49"/>
</g>
<!-- 59d14377fd1646b58c1d7744ff99b3eb&#45;&gt;end -->
<g id="edge6" class="edge">
<title>59d14377fd1646b58c1d7744ff99b3eb:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M65.88,-121.5C65.88,-93.81 67.01,-84.23 67.31,-61.2"/>
<polygon fill="black" stroke="black" points="70.8,-61.53 67.37,-51.51 63.8,-61.49 70.8,-61.53"/>
</g>
<!-- 59d14377fd1646b58c1d7744ff99b3eb&#45;&gt;end -->
<g id="edge7" class="edge">
<title>59d14377fd1646b58c1d7744ff99b3eb:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M85.62,-121.5C85.62,-93.76 89.04,-84.26 89.92,-61.22"/>
<polygon fill="black" stroke="black" points="93.41,-61.58 90.1,-51.51 86.42,-61.45 93.41,-61.58"/>
</g>
<!-- start -->
<g id="node3" class="node">
<title>start</title>
<polygon fill="none" stroke="black" points="29,-415.5 29,-464.5 83,-464.5 83,-415.5 29,-415.5"/>
<text text-anchor="middle" x="55.62" y="-447.2" font-family="Times New Roman,serif" font-size="14.00">start</text>
<polyline fill="none" stroke="black" points="29,-440 82.25,-440"/>
<text text-anchor="middle" x="55.38" y="-422.7" font-family="Times New Roman,serif" font-size="14.00">0</text>
</g>
<!-- start&#45;&gt;332548901fbe4af38afefb4d353e362b -->
<g id="edge1" class="edge">
<title>start:s&#45;&gt;332548901fbe4af38afefb4d353e362b:n</title>
<path fill="none" stroke="black" d="M55.38,-415C55.38,-387.44 55.66,-377.81 55.73,-355.01"/>
<polygon fill="black" stroke="black" points="59.23,-355.02 55.75,-345.01 52.23,-355.01 59.23,-355.02"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 12.2.1 (20241206.2353)
-->
<!-- Pages: 1 -->
<svg width="124pt" height="743pt"
viewBox="0.00 0.00 124.00 742.50" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 738.5)">
<polygon fill="white" stroke="none" points="-4,4 -4,-738.5 120,-738.5 120,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster_7dccb116f4ec46af830d98c0acaf0492</title>
<polygon fill="none" stroke="grey" points="8,-489 8,-645 108,-645 108,-489 8,-489"/>
<text text-anchor="middle" x="58" y="-627.7" font-family="Times New Roman,serif" font-size="14.00" fill="grey">Thread 0</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_3404a0dea51f4e34a210eac11779400b</title>
<polygon fill="none" stroke="grey" points="8,-301.5 8,-457.5 108,-457.5 108,-301.5 8,-301.5"/>
<text text-anchor="middle" x="58" y="-440.2" font-family="Times New Roman,serif" font-size="14.00" fill="grey">Thread 1</text>
</g>
<g id="clust3" class="cluster">
<title>cluster_b67f08b464b449b194ef47038eeb6471</title>
<polygon fill="none" stroke="grey" points="8,-114 8,-270 108,-270 108,-114 8,-114"/>
<text text-anchor="middle" x="58" y="-252.7" font-family="Times New Roman,serif" font-size="14.00" fill="grey">Thread 2</text>
</g>
<!-- fd260036f7d74bacab957af6e8ccbcce -->
<g id="node1" class="node">
<title>fd260036f7d74bacab957af6e8ccbcce</title>
<polygon fill="none" stroke="black" points="16.25,-497.5 16.25,-612 99.75,-612 99.75,-497.5 16.25,-497.5"/>
<text text-anchor="middle" x="57.62" y="-594.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="16.25,-587.5 99.75,-587.5"/>
<text text-anchor="middle" x="58" y="-570.2" font-family="Times New Roman,serif" font-size="14.00">2: Biquad_0</text>
<polyline fill="none" stroke="black" points="16.25,-563 99.75,-563"/>
<text text-anchor="middle" x="58" y="-545.7" font-family="Times New Roman,serif" font-size="14.00">filter_type: </text>
<text text-anchor="middle" x="58" y="-529.2" font-family="Times New Roman,serif" font-size="14.00">type: bypass</text>
<polyline fill="none" stroke="black" points="16.25,-522 99.75,-522"/>
<text text-anchor="middle" x="57.62" y="-504.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- bbffff2f73794aeeb469a1d88b02e9f6 -->
<g id="node2" class="node">
<title>bbffff2f73794aeeb469a1d88b02e9f6</title>
<polygon fill="none" stroke="black" points="16.25,-310 16.25,-424.5 99.75,-424.5 99.75,-310 16.25,-310"/>
<text text-anchor="middle" x="57.62" y="-407.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="16.25,-400 99.75,-400"/>
<text text-anchor="middle" x="58" y="-382.7" font-family="Times New Roman,serif" font-size="14.00">4: Biquad_1</text>
<polyline fill="none" stroke="black" points="16.25,-375.5 99.75,-375.5"/>
<text text-anchor="middle" x="58" y="-358.2" font-family="Times New Roman,serif" font-size="14.00">filter_type: </text>
<text text-anchor="middle" x="58" y="-341.7" font-family="Times New Roman,serif" font-size="14.00">type: bypass</text>
<polyline fill="none" stroke="black" points="16.25,-334.5 99.75,-334.5"/>
<text text-anchor="middle" x="57.62" y="-317.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- fd260036f7d74bacab957af6e8ccbcce&#45;&gt;bbffff2f73794aeeb469a1d88b02e9f6 -->
<g id="edge2" class="edge">
<title>fd260036f7d74bacab957af6e8ccbcce:s&#45;&gt;bbffff2f73794aeeb469a1d88b02e9f6:n</title>
<path fill="none" stroke="black" d="M57.62,-496.5C57.62,-469.14 57.62,-459.57 57.62,-436.93"/>
<polygon fill="black" stroke="black" points="61.13,-437.01 57.63,-427.01 54.13,-437.01 61.13,-437.01"/>
</g>
<!-- 540b25f562304075921a377eae641ad3 -->
<g id="node3" class="node">
<title>540b25f562304075921a377eae641ad3</title>
<polygon fill="none" stroke="black" points="16.25,-122.5 16.25,-237 99.75,-237 99.75,-122.5 16.25,-122.5"/>
<text text-anchor="middle" x="57.62" y="-219.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="16.25,-212.5 99.75,-212.5"/>
<text text-anchor="middle" x="58" y="-195.2" font-family="Times New Roman,serif" font-size="14.00">6: Biquad_2</text>
<polyline fill="none" stroke="black" points="16.25,-188 99.75,-188"/>
<text text-anchor="middle" x="58" y="-170.7" font-family="Times New Roman,serif" font-size="14.00">filter_type: </text>
<text text-anchor="middle" x="58" y="-154.2" font-family="Times New Roman,serif" font-size="14.00">type: bypass</text>
<polyline fill="none" stroke="black" points="16.25,-147 99.75,-147"/>
<text text-anchor="middle" x="57.62" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- bbffff2f73794aeeb469a1d88b02e9f6&#45;&gt;540b25f562304075921a377eae641ad3 -->
<g id="edge3" class="edge">
<title>bbffff2f73794aeeb469a1d88b02e9f6:s&#45;&gt;540b25f562304075921a377eae641ad3:n</title>
<path fill="none" stroke="black" d="M57.62,-309C57.62,-281.64 57.62,-272.07 57.62,-249.43"/>
<polygon fill="black" stroke="black" points="61.13,-249.51 57.63,-239.51 54.13,-249.51 61.13,-249.51"/>
</g>
<!-- end -->
<g id="node5" class="node">
<title>end</title>
<polygon fill="none" stroke="black" points="31,-0.5 31,-49.5 85,-49.5 85,-0.5 31,-0.5"/>
<text text-anchor="middle" x="57.38" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="31,-25 84.5,-25"/>
<text text-anchor="middle" x="57.75" y="-7.7" font-family="Times New Roman,serif" font-size="14.00">end</text>
</g>
<!-- 540b25f562304075921a377eae641ad3&#45;&gt;end -->
<g id="edge4" class="edge">
<title>540b25f562304075921a377eae641ad3:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M57.62,-121.5C57.62,-93.94 57.44,-84.31 57.39,-61.51"/>
<polygon fill="black" stroke="black" points="60.89,-61.51 57.38,-51.51 53.89,-61.52 60.89,-61.51"/>
</g>
<!-- start -->
<g id="node4" class="node">
<title>start</title>
<polygon fill="none" stroke="black" points="31,-685 31,-734 85,-734 85,-685 31,-685"/>
<text text-anchor="middle" x="57.62" y="-716.7" font-family="Times New Roman,serif" font-size="14.00">start</text>
<polyline fill="none" stroke="black" points="31,-709.5 84.25,-709.5"/>
<text text-anchor="middle" x="57.38" y="-692.2" font-family="Times New Roman,serif" font-size="14.00">0</text>
</g>
<!-- start&#45;&gt;fd260036f7d74bacab957af6e8ccbcce -->
<g id="edge1" class="edge">
<title>start:s&#45;&gt;fd260036f7d74bacab957af6e8ccbcce:n</title>
<path fill="none" stroke="black" d="M57.38,-684.5C57.38,-656.94 57.56,-647.31 57.61,-624.51"/>
<polygon fill="black" stroke="black" points="61.11,-624.52 57.62,-614.51 54.11,-624.51 61.11,-624.52"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 12.2.1 (20241206.2353)
-->
<!-- Pages: 1 -->
<svg width="159pt" height="555pt"
viewBox="0.00 0.00 159.00 555.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 551)">
<polygon fill="white" stroke="none" points="-4,4 -4,-551 155,-551 155,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster_e6903db6fd444d1c98585401309db51a</title>
<polygon fill="none" stroke="grey" points="8,-301.5 8,-457.5 108,-457.5 108,-301.5 8,-301.5"/>
<text text-anchor="middle" x="58" y="-440.2" font-family="Times New Roman,serif" font-size="14.00" fill="grey">Thread 0</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_052ef29601934d51b7ea1712c26b8e7a</title>
<polygon fill="none" stroke="grey" points="43,-114 43,-270 143,-270 143,-114 43,-114"/>
<text text-anchor="middle" x="93" y="-252.7" font-family="Times New Roman,serif" font-size="14.00" fill="grey">Thread 1</text>
</g>
<!-- 853016d4423a4b118123671b03fb6352 -->
<g id="node1" class="node">
<title>853016d4423a4b118123671b03fb6352</title>
<polygon fill="none" stroke="black" points="16.25,-310 16.25,-424.5 99.75,-424.5 99.75,-310 16.25,-310"/>
<text text-anchor="middle" x="57.62" y="-407.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="16.25,-400 99.75,-400"/>
<text text-anchor="middle" x="58" y="-382.7" font-family="Times New Roman,serif" font-size="14.00">2: Biquad_0</text>
<polyline fill="none" stroke="black" points="16.25,-375.5 99.75,-375.5"/>
<text text-anchor="middle" x="58" y="-358.2" font-family="Times New Roman,serif" font-size="14.00">filter_type: </text>
<text text-anchor="middle" x="58" y="-341.7" font-family="Times New Roman,serif" font-size="14.00">type: bypass</text>
<polyline fill="none" stroke="black" points="16.25,-334.5 99.75,-334.5"/>
<text text-anchor="middle" x="57.62" y="-317.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- 9a2129a24c2c4e54bf2cc7d7bd1bd74c -->
<g id="node2" class="node">
<title>9a2129a24c2c4e54bf2cc7d7bd1bd74c</title>
<polygon fill="none" stroke="black" points="51.25,-122.5 51.25,-237 134.75,-237 134.75,-122.5 51.25,-122.5"/>
<text text-anchor="middle" x="72.12" y="-219.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="93,-212.5 93,-237"/>
<text text-anchor="middle" x="113.88" y="-219.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="51.25,-212.5 134.75,-212.5"/>
<text text-anchor="middle" x="93" y="-195.2" font-family="Times New Roman,serif" font-size="14.00">4: Biquad_1</text>
<polyline fill="none" stroke="black" points="51.25,-188 134.75,-188"/>
<text text-anchor="middle" x="93" y="-170.7" font-family="Times New Roman,serif" font-size="14.00">filter_type: </text>
<text text-anchor="middle" x="93" y="-154.2" font-family="Times New Roman,serif" font-size="14.00">type: bypass</text>
<polyline fill="none" stroke="black" points="51.25,-147 134.75,-147"/>
<text text-anchor="middle" x="72.12" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="93,-122.5 93,-147"/>
<text text-anchor="middle" x="113.88" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- 853016d4423a4b118123671b03fb6352&#45;&gt;9a2129a24c2c4e54bf2cc7d7bd1bd74c -->
<g id="edge3" class="edge">
<title>853016d4423a4b118123671b03fb6352:s&#45;&gt;9a2129a24c2c4e54bf2cc7d7bd1bd74c:n</title>
<path fill="none" stroke="black" d="M57.62,-309C57.62,-280.94 68.63,-272.33 71.46,-249.29"/>
<polygon fill="black" stroke="black" points="74.95,-249.7 72.04,-239.51 67.96,-249.29 74.95,-249.7"/>
</g>
<!-- end -->
<g id="node4" class="node">
<title>end</title>
<polygon fill="none" stroke="black" points="66,-0.5 66,-49.5 120,-49.5 120,-0.5 66,-0.5"/>
<text text-anchor="middle" x="79.38" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="92.75,-25 92.75,-49.5"/>
<text text-anchor="middle" x="106.12" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">1</text>
<polyline fill="none" stroke="black" points="66,-25 119.5,-25"/>
<text text-anchor="middle" x="92.75" y="-7.7" font-family="Times New Roman,serif" font-size="14.00">end</text>
</g>
<!-- 9a2129a24c2c4e54bf2cc7d7bd1bd74c&#45;&gt;end -->
<g id="edge4" class="edge">
<title>9a2129a24c2c4e54bf2cc7d7bd1bd74c:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M72.12,-121.5C72.12,-93.68 77.63,-84.31 79.04,-61.24"/>
<polygon fill="black" stroke="black" points="82.54,-61.61 79.33,-51.51 75.54,-61.41 82.54,-61.61"/>
</g>
<!-- 9a2129a24c2c4e54bf2cc7d7bd1bd74c&#45;&gt;end -->
<g id="edge5" class="edge">
<title>9a2129a24c2c4e54bf2cc7d7bd1bd74c:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M113.88,-121.5C113.88,-93.66 107.99,-84.32 106.48,-61.25"/>
<polygon fill="black" stroke="black" points="109.98,-61.4 106.17,-51.51 102.99,-61.62 109.98,-61.4"/>
</g>
<!-- start -->
<g id="node3" class="node">
<title>start</title>
<polygon fill="none" stroke="black" points="66,-497.5 66,-546.5 120,-546.5 120,-497.5 66,-497.5"/>
<text text-anchor="middle" x="92.75" y="-529.2" font-family="Times New Roman,serif" font-size="14.00">start</text>
<polyline fill="none" stroke="black" points="66,-522 119.5,-522"/>
<text text-anchor="middle" x="79.38" y="-504.7" font-family="Times New Roman,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="92.75,-497.5 92.75,-522"/>
<text text-anchor="middle" x="106.12" y="-504.7" font-family="Times New Roman,serif" font-size="14.00">1</text>
</g>
<!-- start&#45;&gt;853016d4423a4b118123671b03fb6352 -->
<g id="edge1" class="edge">
<title>start:s&#45;&gt;853016d4423a4b118123671b03fb6352:n</title>
<path fill="none" stroke="black" d="M79.38,-497C79.38,-467.94 62.72,-460.42 58.56,-436.75"/>
<polygon fill="black" stroke="black" points="62.07,-436.68 57.75,-427.01 55.09,-437.26 62.07,-436.68"/>
</g>
<!-- start&#45;&gt;9a2129a24c2c4e54bf2cc7d7bd1bd74c -->
<g id="edge2" class="edge">
<title>start:s&#45;&gt;9a2129a24c2c4e54bf2cc7d7bd1bd74c:n</title>
<path fill="none" stroke="black" d="M106.12,-497C106.12,-479.25 110.62,-475.19 112,-457.5 119.31,-364.13 114.31,-337.65 113.9,-249.38"/>
<polygon fill="black" stroke="black" points="117.4,-249.51 113.88,-239.51 110.4,-249.52 117.4,-249.51"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 12.2.1 (20241206.2353)
-->
<!-- Pages: 1 -->
<svg width="224pt" height="555pt"
viewBox="0.00 0.00 224.00 555.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 551)">
<polygon fill="white" stroke="none" points="-4,4 -4,-551 220,-551 220,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster_fb4404fd272f4d6e81e37c01313fb286</title>
<polygon fill="none" stroke="grey" points="8,-301.5 8,-457.5 208,-457.5 208,-301.5 8,-301.5"/>
<text text-anchor="middle" x="108" y="-440.2" font-family="Times New Roman,serif" font-size="14.00" fill="grey">Thread 0</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_d8debef68bf14a139e453cd5e60e894a</title>
<polygon fill="none" stroke="grey" points="58,-114 58,-270 158,-270 158,-114 58,-114"/>
<text text-anchor="middle" x="108" y="-252.7" font-family="Times New Roman,serif" font-size="14.00" fill="grey">Thread 1</text>
</g>
<!-- 432f839888c64b0e886f3068efbeb00f -->
<g id="node1" class="node">
<title>432f839888c64b0e886f3068efbeb00f</title>
<polygon fill="none" stroke="black" points="16.25,-310 16.25,-424.5 99.75,-424.5 99.75,-310 16.25,-310"/>
<text text-anchor="middle" x="57.62" y="-407.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="16.25,-400 99.75,-400"/>
<text text-anchor="middle" x="58" y="-382.7" font-family="Times New Roman,serif" font-size="14.00">2: Biquad_0</text>
<polyline fill="none" stroke="black" points="16.25,-375.5 99.75,-375.5"/>
<text text-anchor="middle" x="58" y="-358.2" font-family="Times New Roman,serif" font-size="14.00">filter_type: </text>
<text text-anchor="middle" x="58" y="-341.7" font-family="Times New Roman,serif" font-size="14.00">type: bypass</text>
<polyline fill="none" stroke="black" points="16.25,-334.5 99.75,-334.5"/>
<text text-anchor="middle" x="57.62" y="-317.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- ca24f2ba39734dbba653a252011874f3 -->
<g id="node3" class="node">
<title>ca24f2ba39734dbba653a252011874f3</title>
<polygon fill="none" stroke="black" points="66.25,-122.5 66.25,-237 149.75,-237 149.75,-122.5 66.25,-122.5"/>
<text text-anchor="middle" x="87.12" y="-219.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="108,-212.5 108,-237"/>
<text text-anchor="middle" x="128.88" y="-219.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="66.25,-212.5 149.75,-212.5"/>
<text text-anchor="middle" x="108" y="-195.2" font-family="Times New Roman,serif" font-size="14.00">5: Biquad_1</text>
<polyline fill="none" stroke="black" points="66.25,-188 149.75,-188"/>
<text text-anchor="middle" x="108" y="-170.7" font-family="Times New Roman,serif" font-size="14.00">filter_type: </text>
<text text-anchor="middle" x="108" y="-154.2" font-family="Times New Roman,serif" font-size="14.00">type: bypass</text>
<polyline fill="none" stroke="black" points="66.25,-147 149.75,-147"/>
<text text-anchor="middle" x="87.12" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="108,-122.5 108,-147"/>
<text text-anchor="middle" x="128.88" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- 432f839888c64b0e886f3068efbeb00f&#45;&gt;ca24f2ba39734dbba653a252011874f3 -->
<g id="edge3" class="edge">
<title>432f839888c64b0e886f3068efbeb00f:s&#45;&gt;ca24f2ba39734dbba653a252011874f3:n</title>
<path fill="none" stroke="black" d="M57.62,-309C57.62,-278.97 80.41,-273.28 85.93,-249.14"/>
<polygon fill="black" stroke="black" points="89.38,-249.82 86.96,-239.5 82.42,-249.08 89.38,-249.82"/>
</g>
<!-- 280217c22f874ce38519f5f937b10027 -->
<g id="node2" class="node">
<title>280217c22f874ce38519f5f937b10027</title>
<polygon fill="none" stroke="black" points="117.62,-330.5 117.62,-404 200.38,-404 200.38,-330.5 117.62,-330.5"/>
<text text-anchor="middle" x="159" y="-386.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="117.62,-379.5 200.38,-379.5"/>
<text text-anchor="middle" x="159" y="-362.2" font-family="Times New Roman,serif" font-size="14.00">3: Bypass_0</text>
<polyline fill="none" stroke="black" points="117.62,-355 200.38,-355"/>
<text text-anchor="middle" x="159" y="-337.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- 280217c22f874ce38519f5f937b10027&#45;&gt;ca24f2ba39734dbba653a252011874f3 -->
<g id="edge4" class="edge">
<title>280217c22f874ce38519f5f937b10027:s&#45;&gt;ca24f2ba39734dbba653a252011874f3:n</title>
<path fill="none" stroke="black" d="M159,-329.5C159,-290.7 134.26,-282.24 129.62,-249.13"/>
<polygon fill="black" stroke="black" points="133.14,-249.25 128.98,-239.51 126.15,-249.72 133.14,-249.25"/>
</g>
<!-- end -->
<g id="node5" class="node">
<title>end</title>
<polygon fill="none" stroke="black" points="81,-0.5 81,-49.5 135,-49.5 135,-0.5 81,-0.5"/>
<text text-anchor="middle" x="94.38" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="107.75,-25 107.75,-49.5"/>
<text text-anchor="middle" x="121.12" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">1</text>
<polyline fill="none" stroke="black" points="81,-25 134.5,-25"/>
<text text-anchor="middle" x="107.75" y="-7.7" font-family="Times New Roman,serif" font-size="14.00">end</text>
</g>
<!-- ca24f2ba39734dbba653a252011874f3&#45;&gt;end -->
<g id="edge5" class="edge">
<title>ca24f2ba39734dbba653a252011874f3:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M87.12,-121.5C87.12,-93.68 92.63,-84.31 94.04,-61.24"/>
<polygon fill="black" stroke="black" points="97.54,-61.61 94.33,-51.51 90.54,-61.41 97.54,-61.61"/>
</g>
<!-- ca24f2ba39734dbba653a252011874f3&#45;&gt;end -->
<g id="edge6" class="edge">
<title>ca24f2ba39734dbba653a252011874f3:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M128.88,-121.5C128.88,-93.66 122.99,-84.32 121.48,-61.25"/>
<polygon fill="black" stroke="black" points="124.98,-61.4 121.17,-51.51 117.99,-61.62 124.98,-61.4"/>
</g>
<!-- start -->
<g id="node4" class="node">
<title>start</title>
<polygon fill="none" stroke="black" points="81,-497.5 81,-546.5 135,-546.5 135,-497.5 81,-497.5"/>
<text text-anchor="middle" x="107.75" y="-529.2" font-family="Times New Roman,serif" font-size="14.00">start</text>
<polyline fill="none" stroke="black" points="81,-522 134.5,-522"/>
<text text-anchor="middle" x="94.38" y="-504.7" font-family="Times New Roman,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="107.75,-497.5 107.75,-522"/>
<text text-anchor="middle" x="121.12" y="-504.7" font-family="Times New Roman,serif" font-size="14.00">1</text>
</g>
<!-- start&#45;&gt;432f839888c64b0e886f3068efbeb00f -->
<g id="edge1" class="edge">
<title>start:s&#45;&gt;432f839888c64b0e886f3068efbeb00f:n</title>
<path fill="none" stroke="black" d="M94.38,-497C94.38,-465.46 65.73,-461.73 59.02,-436.71"/>
<polygon fill="black" stroke="black" points="62.52,-436.49 57.81,-427 55.58,-437.36 62.52,-436.49"/>
</g>
<!-- start&#45;&gt;280217c22f874ce38519f5f937b10027 -->
<g id="edge2" class="edge">
<title>start:s&#45;&gt;280217c22f874ce38519f5f937b10027:n</title>
<path fill="none" stroke="black" d="M121.12,-497C121.12,-456.93 152.23,-450.24 158.06,-416.43"/>
<polygon fill="black" stroke="black" points="161.55,-416.76 158.88,-406.51 154.57,-416.19 161.55,-416.76"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 12.2.1 (20241206.2353)
-->
<!-- Pages: 1 -->
<svg width="230pt" height="555pt"
viewBox="0.00 0.00 230.00 555.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 551)">
<polygon fill="white" stroke="none" points="-4,4 -4,-551 226,-551 226,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster_9fa03a3b50db4b63b5506cb9141a2750</title>
<polygon fill="none" stroke="grey" points="8,-301.5 8,-457.5 108,-457.5 108,-301.5 8,-301.5"/>
<text text-anchor="middle" x="58" y="-440.2" font-family="Times New Roman,serif" font-size="14.00" fill="grey">Thread 0</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_715c255746144fb49eb8295a63cf7e8a</title>
<polygon fill="none" stroke="grey" points="116,-322 116,-437 214,-437 214,-322 116,-322"/>
<text text-anchor="middle" x="165" y="-419.7" font-family="Times New Roman,serif" font-size="14.00" fill="grey">Thread 1</text>
</g>
<g id="clust3" class="cluster">
<title>cluster_339a5a31d40f4f5ab28af2071e69e811</title>
<polygon fill="none" stroke="grey" points="61,-114 61,-270 161,-270 161,-114 61,-114"/>
<text text-anchor="middle" x="111" y="-252.7" font-family="Times New Roman,serif" font-size="14.00" fill="grey">Thread 2</text>
</g>
<!-- addd83d1fc20459eb0b3b34cbdead483 -->
<g id="node1" class="node">
<title>addd83d1fc20459eb0b3b34cbdead483</title>
<polygon fill="none" stroke="black" points="16.25,-310 16.25,-424.5 99.75,-424.5 99.75,-310 16.25,-310"/>
<text text-anchor="middle" x="57.62" y="-407.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="16.25,-400 99.75,-400"/>
<text text-anchor="middle" x="58" y="-382.7" font-family="Times New Roman,serif" font-size="14.00">2: Biquad_0</text>
<polyline fill="none" stroke="black" points="16.25,-375.5 99.75,-375.5"/>
<text text-anchor="middle" x="58" y="-358.2" font-family="Times New Roman,serif" font-size="14.00">filter_type: </text>
<text text-anchor="middle" x="58" y="-341.7" font-family="Times New Roman,serif" font-size="14.00">type: bypass</text>
<polyline fill="none" stroke="black" points="16.25,-334.5 99.75,-334.5"/>
<text text-anchor="middle" x="57.62" y="-317.2" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- 16ebb0098ef041108b40ab5dc53c13f3 -->
<g id="node3" class="node">
<title>16ebb0098ef041108b40ab5dc53c13f3</title>
<polygon fill="none" stroke="black" points="69.25,-122.5 69.25,-237 152.75,-237 152.75,-122.5 69.25,-122.5"/>
<text text-anchor="middle" x="90.12" y="-219.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="111,-212.5 111,-237"/>
<text text-anchor="middle" x="131.88" y="-219.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="69.25,-212.5 152.75,-212.5"/>
<text text-anchor="middle" x="111" y="-195.2" font-family="Times New Roman,serif" font-size="14.00">6: Biquad_1</text>
<polyline fill="none" stroke="black" points="69.25,-188 152.75,-188"/>
<text text-anchor="middle" x="111" y="-170.7" font-family="Times New Roman,serif" font-size="14.00">filter_type: </text>
<text text-anchor="middle" x="111" y="-154.2" font-family="Times New Roman,serif" font-size="14.00">type: bypass</text>
<polyline fill="none" stroke="black" points="69.25,-147 152.75,-147"/>
<text text-anchor="middle" x="90.12" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="111,-122.5 111,-147"/>
<text text-anchor="middle" x="131.88" y="-129.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- addd83d1fc20459eb0b3b34cbdead483&#45;&gt;16ebb0098ef041108b40ab5dc53c13f3 -->
<g id="edge3" class="edge">
<title>addd83d1fc20459eb0b3b34cbdead483:s&#45;&gt;16ebb0098ef041108b40ab5dc53c13f3:n</title>
<path fill="none" stroke="black" d="M57.62,-309C57.62,-278.5 82.73,-273.58 88.81,-249.27"/>
<polygon fill="black" stroke="black" points="92.27,-249.84 89.95,-239.5 85.31,-249.03 92.27,-249.84"/>
</g>
<!-- e47c297e048a4a7c9961bf18eda961ba -->
<g id="node2" class="node">
<title>e47c297e048a4a7c9961bf18eda961ba</title>
<polygon fill="none" stroke="black" points="123.62,-330.5 123.62,-404 206.38,-404 206.38,-330.5 123.62,-330.5"/>
<text text-anchor="middle" x="165" y="-386.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="123.62,-379.5 206.38,-379.5"/>
<text text-anchor="middle" x="165" y="-362.2" font-family="Times New Roman,serif" font-size="14.00">4: Bypass_0</text>
<polyline fill="none" stroke="black" points="123.62,-355 206.38,-355"/>
<text text-anchor="middle" x="165" y="-337.7" font-family="Times New Roman,serif" font-size="14.00"> </text>
</g>
<!-- e47c297e048a4a7c9961bf18eda961ba&#45;&gt;16ebb0098ef041108b40ab5dc53c13f3 -->
<g id="edge4" class="edge">
<title>e47c297e048a4a7c9961bf18eda961ba:s&#45;&gt;16ebb0098ef041108b40ab5dc53c13f3:n</title>
<path fill="none" stroke="black" d="M165,-329.5C165,-290.31 137.79,-282.52 132.69,-249.22"/>
<polygon fill="black" stroke="black" points="136.2,-249.23 131.99,-239.51 129.22,-249.74 136.2,-249.23"/>
</g>
<!-- end -->
<g id="node5" class="node">
<title>end</title>
<polygon fill="none" stroke="black" points="84,-0.5 84,-49.5 138,-49.5 138,-0.5 84,-0.5"/>
<text text-anchor="middle" x="97.38" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="110.75,-25 110.75,-49.5"/>
<text text-anchor="middle" x="124.12" y="-32.2" font-family="Times New Roman,serif" font-size="14.00">1</text>
<polyline fill="none" stroke="black" points="84,-25 137.5,-25"/>
<text text-anchor="middle" x="110.75" y="-7.7" font-family="Times New Roman,serif" font-size="14.00">end</text>
</g>
<!-- 16ebb0098ef041108b40ab5dc53c13f3&#45;&gt;end -->
<g id="edge5" class="edge">
<title>16ebb0098ef041108b40ab5dc53c13f3:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M90.12,-121.5C90.12,-93.68 95.63,-84.31 97.04,-61.24"/>
<polygon fill="black" stroke="black" points="100.54,-61.61 97.33,-51.51 93.54,-61.41 100.54,-61.61"/>
</g>
<!-- 16ebb0098ef041108b40ab5dc53c13f3&#45;&gt;end -->
<g id="edge6" class="edge">
<title>16ebb0098ef041108b40ab5dc53c13f3:s&#45;&gt;end:n</title>
<path fill="none" stroke="black" d="M131.88,-121.5C131.88,-93.66 125.99,-84.32 124.48,-61.25"/>
<polygon fill="black" stroke="black" points="127.98,-61.4 124.17,-51.51 120.99,-61.62 127.98,-61.4"/>
</g>
<!-- start -->
<g id="node4" class="node">
<title>start</title>
<polygon fill="none" stroke="black" points="84,-497.5 84,-546.5 138,-546.5 138,-497.5 84,-497.5"/>
<text text-anchor="middle" x="110.75" y="-529.2" font-family="Times New Roman,serif" font-size="14.00">start</text>
<polyline fill="none" stroke="black" points="84,-522 137.5,-522"/>
<text text-anchor="middle" x="97.38" y="-504.7" font-family="Times New Roman,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="110.75,-497.5 110.75,-522"/>
<text text-anchor="middle" x="124.12" y="-504.7" font-family="Times New Roman,serif" font-size="14.00">1</text>
</g>
<!-- start&#45;&gt;addd83d1fc20459eb0b3b34cbdead483 -->
<g id="edge1" class="edge">
<title>start:s&#45;&gt;addd83d1fc20459eb0b3b34cbdead483:n</title>
<path fill="none" stroke="black" d="M97.38,-497C97.38,-464.9 66.4,-462.09 59.13,-436.86"/>
<polygon fill="black" stroke="black" points="62.61,-436.45 57.82,-427 55.67,-437.37 62.61,-436.45"/>
</g>
<!-- start&#45;&gt;e47c297e048a4a7c9961bf18eda961ba -->
<g id="edge2" class="edge">
<title>start:s&#45;&gt;e47c297e048a4a7c9961bf18eda961ba:n</title>
<path fill="none" stroke="black" d="M124.12,-497C124.12,-456.28 157.99,-450.54 164.07,-416.1"/>
<polygon fill="black" stroke="black" points="167.53,-416.77 164.87,-406.51 160.55,-416.18 167.53,-416.77"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@@ -0,0 +1,67 @@
Lib Audio DSP
#############
.. raw:: latex
\newpage
.. rubric:: Introduction
.. note::
Some software components in this tool flow are prototypes and will be updated in Version 2 of the library.
The underlying Digital Signal Processing (DSP) blocks are however fully functional. Future updates will
enhance the features and flexibility of the design tool.
lib_audio_dsp is a DSP library for the XMOS xcore architecture. It facilitates the creation of
multithreaded audio DSP pipelines that efficiently utilise the xcore architecture.
The library is built around a set of DSP function blocks, referred to in the documentation as *Stages*,
which have a consistent API and can be combined to create many different designs.
A tool for easily
combining stages into a custom DSP pipeline is provided. DSP pipeline parameters can be adjusted and
tuned on the fly via a PC based tuning interface, and utilities for hardware controls are also provided.
lib_audio_dsp includes common signal processing functions optimised for the xcore, such as:
* biquads and FIR filters.
* compressors, limiters, noise gates and envelope detectors.
* adders, subtractors, gains, volume controls and mixers.
* delays and reverb.
These can be combined together to make complex audio pipelines for many
different applications, such as home audio, music production, voice
processing, and AI feature extraction.
This document covers the following topics:
#. :ref:`tool_user_guide_section`: A beginner's guide to installing and using the DSP design and generation Python library.
#. :ref:`design_guide_section`: Advanced guidance on designing and debugging generated DSP pipelines.
#. :ref:`dsp_components_section`: List of all DSP components and details on the backend implementation.
#. :ref:`run_time_control_guide_section`: Basic guide to add time control to a DSP application.
#. :ref:`api_reference_section`: References to DSP components, control and integration and high-level tool desing API.
The subsequent sections provide comprehensive insights into the functionalities and applications of lib_audio_dsp,
detailing how to leverage its features for efficient audio signal processing.
For example appliations, see the Application Notes related to lib_audio_dsp on the
`XMOS website <https://www.xmos.com/file/lib_audio_dsp#related-application-notes>`_.
.. toctree::
:maxdepth: 2
:hidden:
01_tool_user_guide/index
02_design_guide/index
03_dsp_components/index
04_run_time_control_guide/index
05_api_reference/index
.. rubric:: Copyright & Disclaimer
|XMOS copyright|
|XMOS disclaimer|
|XMOS trademarks|