blob: 53588dd61abc6cb42f232f29793f70f2b342a58c [file] [log] [blame] [view]
# Audio Signal Processing
## Overview
The signal processing interface is available to be potentially used by audio composite drivers.
This interface `SignalProcessing` is a FIDL protocol used by the `Composite` protocol to provide
audio signal processing capabilities.
The `SignalProcessing` protocol is defined to control signal processing hardware and their
topologies. We define processing elements (PEs) as a logical unit of audio data processing provided
by an audio driver, and we define topologies as the arrangement of PEs in [pipelines][pipeline] and
controls associated with them.
The `SignalProcessing` protocol allows hardware vendors to implement drivers with stable
application binary interfaces (ABIs), and allow system integrators to configure drivers to perform
differently based on system or product requirements using these interfaces for run-time
configurations.
The `SignalProcessing` protocol composes the `Reader` signal processing protocol. Signal processing
methods that only retrieve information are part of the `Reader` protocol, the rest are part of the
`SignalProcessing` protocol itself. This separation allows clients of this interface to compose the
`Reader` signal processing protocol into their own protocol if they require providing a read only
subset of functionality to their own clients.
The `SignalProcessing` protocol and associated definitions are part of the
[fuchsia.hardware.audio.signalprocessing](/sdk/fidl/fuchsia.hardware.audio.signalprocessing)
FIDL library.
### Topologies
Each driver can have its own topology. Drivers can abstract from applications the topologies
exposed by other drivers as needed for a particular configuration or product. Note that it is
possible although not required to expose topologies to applications, in particular to `audio_core`.
Notes:
* Topologies are not meant to fully describe the audio pipeline state/format/configuration
in and out of every PE. The intent is to describe what can be changed/rearranged by the client
based on its knowledge, configuration (for instance from metadata) and specific business logic.
* Topologies used for audio drivers providing the `Composite` protocol must include `ENDPOINT`
PEs that provide an id for the driver's supported ring buffers and DAI interconnects.
### Processing Elements
A PE (defined in the `fuchsia.hardware.audio.signalprocessing` FIDL library as `Element`) is
expected to be hardware-provided functionality managed by a particular driver (but it could be
emulated in software, as any other driver functionality). A pipeline is composed of one or more PEs
and a topology is composed of one or more pipelines.
We refer to the server as the driver that is providing the signal processing protocol.
We refer to the client as the user of the functionality, e.g. an application such as `audio_core`.
## Basic operation
The client is responsible for requesting and then configuring any signal processing capabilities.
Once the server provides its PEs by replying to a client's `GetElements`, the client may
issue `WatchElement` calls (see [hanging get pattern][hanging-get]) to retrieve
PE state and `SetElementState` to dynamically control the PEs parameters as needed. For
instance, to retrieve the `gain` of a PE of `type` `GAIN`, the client issues
`WatchElement` calls, one to retrieve the initial state (the driver will reply to the
first `WatchElement` sent by the client), and subsequent ones to get notified of updates
to the `ElementState` that includes the `gain`. Similarly, to retrieve the state of a PE
of `type` `EQUALIZER`, which is composed of multiple bands in its `bands_state`, a client would
issue a `WatchElement` that would retrieve the initial state (the driver will reply to the
first `WatchElement` sent by the client) including for instance `frequency` fields for
each band.
Also after the server provides its PEs by replying to a client's `GetElements`, the client
may request available topologies with the `GetTopologies` method. If more than one topology is
returned by `GetTopologies`, then `SetTopology` can be used to pick the topology to use.
### GetElements
`GetElements` allows to optionally get a list of all PEs. For instance this method may
be called by a client on a driver abstracting a hardware codec. Once the list of PEs is known to
the client, the client may configure the PEs based on the parameters exposed by the PE types.
### SetElementState
`SetElementState` allows a client to control the state of a PE using an id returned by
`GetElements`. PEs of different types may have different state exposed to clients, the
`SetElementState` parameter `state` has a different type depending on the type of PE.
### WatchElement
`WatchElement` allows a client to monitor the state of a PE using an id returned by
`GetElements`. PEs of different types may have different state exposed to clients, the
`WatchElement` parameter `state` has a different type depending on the type of PE.
The `state` of a PE is composed of values that may be changed directly by the client via a call to
`SetElement`, or indirectly for instance by a calling `SetElement` on a
different PE, or independent of the client for instance due to a plug detect change.
### GetTopologies
`GetTopologies` allows to optionally get a list of topologies. For instance this method may be
called by a client on a driver abstracting a hardware codec. Once the list of topologies is known to
the client, the client may configure the server to use a particular topology.
### SetTopology
`SetTopology` allows a client to control the which topology is used by the server. Only one
topology can be selected at any time.
## Processing elements types
The PEs returned by `GetElements` support a number of different types of signal processing
defined by the PE types and parameters. PE types define standard signal processing (e.g. `GAIN`,
`DELAY`, `EQUALIZER`, etc), vendor specific signal processing (`VENDOR_SPECIFIC` e.g. a type not
defined in the `SignalProcessing` protocol) and `CONNECTION_POINT`s/`ENDPOINT`s used to construct
multi-pipelines topologies (allow for pipelines start, end, routing and mixing definitions, see
[Connection points](#connection-points) and [Endpoints](#endpoints)} below).
Each individual PE may have one or more inputs and one or more output channels. For routing and
mixing, PEs may make the number of output channels different from the number of input channels.
Data in each channel (a.k.a. the signal that is processed) may be altered by the PE. For instance
if there is a single PE of type `AGL` in a pipeline that includes an `ENDPOINT` of type
`DAI_INTERCONNECT` with `DaiFormat` `number_of_channels` set to 2, then AGL (Automatic Gain
Limiting) can be enabled or disabled for these 2 channels by a client calling `SetElementState`
with `state` `enable` set to true or false (this assumes the AGL `Element`s `can_disable` was set
to true).
If optional fields in the different PE types are not included, then the state of the processing
element is not changed with respect to the particular field. For instance, if an
`EqualizerBandState` in a `SetElement` does not include an optional `frequency` then the
equalizer's band frequency state is not changed.
## Vendor specific data
`ElementState` `vendor_specific_data` is an optional parameter that can be specified for any
processing element. This allows processing elements to specify an opaque object to be either sent
to the drivers part of a `SetElementState` or received from a driver as part of a
`WatchElementState`.
In addition to opaque data for any type, a processing element of type `VENDOR_SPECIFIC` allows
drivers to specify a type that is not defined in the `SignalProcessing` protocol, for instance
something that is not standard yet or is not meant to be standardized and provided only by a
specific vendor. A processing element of type `VENDOR_SPECIFIC` does not specify any
`TypeSpecificElement` parameter, instead it may specify opaque data to be sent or received to or
from the driver using the `ElementState` `vendor_specific_data` parameter same as any other
processing element type.
## Topologies {#topologies}
The topologies returned by `GetTopologies` support different arrangements for the PEs returned by
`GetElements`. `GetTopologies` may advertise one or multiple topologies.
### One topology
If one topology is advertised, i.e. `GetTopologies` returns a vector with one element, then all PEs
are part of this explicit single pipeline. Ordering in this case is explicit. For instance, if
`GetElements` returns 2 PEs:
1. `Element`: id = 1, type = `AUTOMATIC_GAIN_LIMITER` (AGL)
1. `Element`: id = 2, type = `EQUALIZER` (EQ)
The one `Topology` element returned by `GetTopologies` will list an `id` and a
`processing_elements_edge_pairs` vector explicitly advertising the order in which signal processing
is performed, in this example:
1. `Topology`: id = 1, `processing_elements_edge_pairs` = vector with one element with
`processing_element_id_from` = 1 and `processing_element_id_to` = 2.
This advertises this one topology with one pipeline:
+-------+ +-------+
Input signal -> | AGL | -> + EQ | -> Output signal
+-------+ +-------+
In this topology the beginning (where the input signal is input into the pipeline) and the end of
the pipeline (where the output signal is output from the pipeline) are implicit. They can be made
explicit with PEs of type `ENDPOINT` (see [Endpoints](#endpoints) below).
If only one topology is advertised, then the contents are informational only since the client can't
change the use of one and only topology.
### Multiple topologies {#multiple-topologies}
If multiple topologies are advertised, i.e. `GetTopologies` returns a vector with multiple element,
then PEs may be used in multiple configurations, i.e. topologies. Each topology explicitly lists
a number of PEs and their ordering, i.e. ordering in this case is explicit. The arrangement and
ordering of PEs define a pipeline.
By listing only the specific arrangements and ordering of PEs supported, servers restrict what
combination of pipelines are valid.
For instance, if `GetElements` returns 6 PEs:
1. `Element`: id = 1, type = `AUTOMATIC_GAIN_LIMITER` (AGL)
1. `Element`: id = 2, type = `EQUALIZER` (EQ)
1. `Element`: id = 3, type = `SAMPLE_RATE_CONVERSION` (SRC)
1. `Element`: id = 4, type = `GAIN`
1. `Element`: id = 5, type = `DYNAMIC_RANGE_COMPRESSION` (DRC1)
1. `Element`: id = 6, type = `DYNAMIC_RANGE_COMPRESSION` (DRC2) parameters different from
DRC1 parameters.
The `Topology` elements returned by `GetTopologies` will list an `id` and a
`processing_elements_edge_pairs` for each topology, in this example:
1. `Topology`: id = 1, `processing_elements_edge_pairs` =
*. processing_element_id_from` = 3 and `processing_element_id_to` = 2.
*. processing_element_id_from` = 2 and `processing_element_id_to` = 4.
*. processing_element_id_from` = 4 and `processing_element_id_to` = 5.
*. processing_element_id_from` = 5 and `processing_element_id_to` = 1.
1. `Topology`: id = 2, `processing_elements_edge_pairs` =
*. processing_element_id_from` = 2 and `processing_element_id_to` = 4.
*. processing_element_id_from` = 4 and `processing_element_id_to` = 6.
This advertises two topologies with one pipeline each:
+-------+ +-------+ +-------+ +-------+ +-------+
Input signal -> | SRC | -> + EQ | -> + GAIN | -> + DRC1 | -> + AGL | -> Output signal
+-------+ +-------+ +-------+ +-------+ +-------+
+-------+ +-------+ +-------+
Input signal -> | EQ | -> + GAIN | -> + DRC2 | -> Output signal
+-------+ +-------+ +-------+
## Connection points {#connection-points}
The PEs of type `CONNECTION_POINT` allow for:
1. Mixing multiple channels within a single pipeline.
1. Mixing multiple channels from different pipelines.
1. Repeating channels.
1. Expanding a single pipeline into multiple pipelines ones (scatter).
{% comment %}
// TODO(https://fxbug.dev/42143529): Add extra context for multi-pipeline construction.
{% endcomment %}
## Endpoints {#endpoints}
The PEs of type `ENDPOINT` are optional (even in the presence of `CONNECTION_POINT`s) and allow for
completing the pipelines structures with a clear starting input(s) and ending output(s). However for
drivers providing the `Composite` protocol, any supported ring buffer or DAI interconnect must be
listed as an `ENDPOINT` with type `RING_BUFFER` and `DAI_INTERCONNECT` returned by `GetElements`.
The endpoint PE id is needed by the `Composite` protocol APIs to identify the ring buffers and DAI
interonnect configurations.
If no `ENDPOINT` is specified, then a PE with no incoming edges is an input and a PE with no
outgoing edges is an output. For instance, the example in
[Multiple topologies](#multiple-topologies) above includes two topologies each with a single
pipeline, the single pipeline in topology id 1 starts with PE id 3 and ends with PE id 1, and the
single pipeline in topology id 2 starts with PE id 2 and ends with PE id 6.
<!-- Reference links -->
[pipeline]: https://en.wikipedia.org/wiki/Pipeline_(computing)
[hanging-get]: /docs/development/api/fidl.md#hanging-get