| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| library fuchsia.hardware.audio.signalprocessing; |
| |
| using zx; |
| |
| // TODO(https://fxbug.dev/42143529): Complete parameters and types for processing elements. |
| |
| alias ElementId = uint64; |
| alias TopologyId = uint64; |
| |
| const MAX_COUNT_PROCESSING_ELEMENTS uint32 = 64; |
| const MAX_COUNT_TOPOLOGIES uint32 = 64; |
| const MAX_COUNT_PROCESSING_ELEMENTS_EDGE_PAIRS uint32 = 64; |
| const MAX_STRING_SIZE uint32 = 256; |
| @available(added=27) |
| const MAX_BYTES_ELEMENT_VENDOR_SPECIFIC uint32 = 4096; |
| |
| type ElementType = flexible enum { |
| /// Vendor Specific. A type of processing element not covered by any subsequent type definition. |
| VENDOR_SPECIFIC = 1; |
| |
| /// Controls pipelines channel mixing and routing. |
| CONNECTION_POINT = 3; |
| |
| /// Gain control, a.k.a. Volume control. |
| GAIN = 4; |
| |
| /// Automatic Gain Control. |
| /// Automatically maintains a suitable signal level regardless of variation of its input. |
| AUTOMATIC_GAIN_CONTROL = 5; |
| |
| /// Automatic Gain Limiter. |
| /// Automatically maintains a signal level below a level specified. |
| /// Input below the level is unaffected, and peaks above the level are attenuated. |
| AUTOMATIC_GAIN_LIMITER = 6; |
| |
| /// Alters the dynamic range of the signal, e.g. dynamic range compression. |
| DYNAMICS = 7; |
| |
| /// Mute. |
| MUTE = 8; |
| |
| /// Delay. |
| DELAY = 9; |
| |
| /// Equalizer. |
| EQUALIZER = 10; |
| |
| /// Sample Rate Conversion. |
| SAMPLE_RATE_CONVERSION = 11; |
| |
| /// Ring Buffer. |
| /// This is the first of two types of elements that can start/end processing pipelines. |
| RING_BUFFER = 13; |
| |
| /// Digital Audio Interface Interconnect. |
| /// This is the second of two types of elements that can start/end processing pipelines. |
| DAI_INTERCONNECT = 14; |
| }; |
| |
| /// Type-specific Parameters for an `Element`. |
| type TypeSpecificElement = flexible union { |
| // See vendor_specific.fidl. |
| 1: vendor_specific VendorSpecific; |
| |
| // See gain.fidl. |
| 2: gain Gain; |
| |
| // See equalizer.fidl. |
| 3: equalizer Equalizer; |
| |
| // See dynamics.fidl. |
| 4: dynamics Dynamics; |
| |
| // See dai_interconnect.fidl. |
| 6: dai_interconnect DaiInterconnect; |
| }; |
| |
| type Element = table { |
| /// Unique ID for this element. The scope of this id is only within the `SignalProcessing` |
| /// protocol lifespan, i.e. until the channel associated with the protocol is closed. |
| /// |
| /// Required. |
| 1: id ElementId; |
| |
| /// Processing element type. |
| /// |
| /// Required. |
| 2: type ElementType; |
| |
| /// Type-specific parameters for the processing element. |
| /// |
| /// Required for `ElementType`s DAI_INTERCONNECT, DYNAMICS, EQUALIZER, GAIN, VENDOR_SPECIFIC. |
| /// Invalid if specified for elements of type AUTOMATIC_GAIN_CONTROL, AUTOMATIC_GAIN_LIMITER, |
| /// CONNECTION_POINT, DELAY, MUTE, RING_BUFFER or SAMPLE_RATE_CONVERSION. |
| 3: type_specific TypeSpecificElement; |
| |
| /// If included, a textual description of the processing element. |
| /// |
| /// Optional. If present, must not be empty. |
| 5: description string:MAX_STRING_SIZE; |
| |
| /// If true, the processing element can be stopped via `SetElementState`. |
| /// If not included or false, the processing element is always started. |
| /// |
| /// Optional. |
| 6: can_stop bool; |
| |
| /// If true, the processing element can be bypassed via `SetElementState`. |
| /// If not included or false, the processing element cannot be bypassed. |
| /// By definition, elements of type DAI_INTERCONNECT or RING_BUFFER cannot be bypassed and |
| /// must never set this field to true. |
| /// |
| /// Optional. |
| 7: can_bypass bool; |
| }; |
| |
| /// Type-specific processing element state, as returned from the driver. |
| /// The type of processing element control is defined by the type of parameters provided in this |
| /// union. This type-specific variant must match the `ElementType` entry in the corresponding |
| /// `Element`. |
| type TypeSpecificElementState = flexible union { |
| // See vendor_specific.fidl. |
| 1: vendor_specific VendorSpecificState; |
| |
| // See gain.fidl. |
| 2: gain GainElementState; |
| |
| // See equalizer.fidl. |
| 3: equalizer EqualizerElementState; |
| |
| // See dynamics.fidl. |
| 4: dynamics DynamicsElementState; |
| |
| // See dai_interconnect.fidl. |
| 6: dai_interconnect DaiInterconnectElementState; |
| }; |
| |
| /// The current state of an element, as returned from the driver. Note that this table contains |
| /// fields that are not present in `SettableElementState`, since they cannot be changed by clients. |
| type ElementState = table { |
| /// Type-specific state parameters for the processing element. |
| /// |
| /// If this processing element is disabled and its type-specific state is provided, then the |
| /// type-specific state is only informational (e.g. the state of a stopped element, if it were |
| /// to be re-started without also providing additional superceding state information). |
| /// |
| /// Required for DAI_INTERCONNECT, DYNAMICS, EQUALIZER, GAIN and VENDOR_SPECIFIC elements. |
| /// Invalid if specified for elements of type AUTOMATIC_GAIN_CONTROL, AUTOMATIC_GAIN_LIMITER, |
| /// CONNECTION_POINT, DELAY, MUTE, RING_BUFFER or SAMPLE_RATE_CONVERSION. |
| 1: type_specific TypeSpecificElementState; |
| |
| /// If included, an opaque object of octets for conveying vendor-specific information from the |
| /// driver to `SignalProcessing` clients. |
| /// |
| /// Optional (permitted even if the element's type is not VENDOR_SPECIFIC). |
| @available(added=27) |
| 4: vendor_specific_data vector<uint8>:MAX_BYTES_ELEMENT_VENDOR_SPECIFIC; |
| |
| /// The start/stop state for this processing element. |
| /// If true, the hardware associated with the element is started. If false, it is stopped. |
| /// |
| /// If the corresponding `Element` omitted `can_stop` or set it to `false`, then this field |
| /// can never be `false`. |
| /// |
| /// A stopped processing element does not provide its abstracted functionality. |
| /// No audio data flows through stopped elements. |
| /// |
| /// Required. |
| 5: started bool; |
| |
| /// The bypass state for this processing element. |
| /// If true, the hardware associated with the element is bypassed. If false or missing, the |
| /// associated hardware is not bypassed. |
| /// |
| /// By default, processing elements are not bypassed. |
| /// If the corresponding `Element` omitted `can_bypass` or set it to `false`, then this field |
| /// can never be set to `true`. |
| /// |
| /// A bypassed element does not affect the flow of audio through the topology. |
| /// Audio flows through a bypassed element, unchanged. |
| /// |
| /// Optional. |
| 6: bypassed bool; |
| |
| /// If included, the driver's best estimate of the amount of time it takes the element's |
| /// hardware to enter a fully operational mode after `started` has changed from false to true. |
| /// Hardware may require some duration to reach a fully operational mode after changing its |
| /// power state, for example. |
| /// |
| /// If `turn_on_delay` is not taken into account, then an audio stream's initial frames might |
| /// be lost while audio elements are powering up. |
| /// If not included, `turn_on_delay` is unknown. |
| /// |
| /// Optional. If specified, must be non-negative. |
| 7: turn_on_delay zx.Duration; |
| |
| /// If included, the driver's best estimate of the amount of time it takes the element's |
| /// hardware to enter a fully disabled mode after `started` has changed from true to false. |
| /// Hardware may require some duration to get into a fully stopped state after a change in |
| /// power state, for example. |
| /// |
| /// If `turn_off_delay` is not taken into account, more frames will be emitted/captured than a |
| /// client might expect, while audio elements are powering down. |
| /// If not included, `turn_off_delay` is unknown. |
| /// |
| /// Optional. If specified, must be non-negative. |
| 8: turn_off_delay zx.Duration; |
| |
| /// If included, the driver's best estimate of the delay added by this processing element, |
| /// as it is currently configured (including `bypassed` state). |
| /// |
| /// This value should be taken into account by timing-sensitive clients, when determining the |
| /// requirements for (playback) minimum lead time and minimum capture delay. |
| /// |
| /// For an element of type `RING_BUFFER`, this delay should not include the inherent delay |
| /// added by the temporary buffering needed to copy data in and out of a ring buffer, which |
| /// is contained in the `RingBufferProperties` field `driver_transfer_bytes`. |
| /// |
| /// Optional. If specified, must be non-negative. |
| 9: processing_delay zx.Duration; |
| }; |
| |
| /// Type-specific processing element state that can be set by clients. |
| /// The type of processing element control is defined by the type of parameters provided in this |
| /// union. This type-specific variant must match the `ElementType` entry in the corresponding |
| /// `Element`. |
| type SettableTypeSpecificElementState = flexible union { |
| // See vendor_specific.fidl. |
| 1: vendor_specific VendorSpecificState; |
| |
| // See gain.fidl. |
| 2: gain GainElementState; |
| |
| // See equalizer.fidl. |
| 3: equalizer EqualizerElementState; |
| |
| // See dynamics.fidl. |
| 4: dynamics DynamicsElementState; |
| }; |
| |
| /// Processing element state that can be set by clients. |
| type SettableElementState = table { |
| /// Type-specific element-state parameters that can be set by clients. |
| /// |
| /// If an element is disabled, changes in this field (and all others in SettableElementState) |
| /// are purely informational and take no effect until the element is enabled. |
| /// |
| /// If not set, then the element's previous `type_specific` state is preserved. |
| /// |
| /// Optional for DYNAMICS, EQUALIZER, GAIN and VENDOR_SPECIFIC types. |
| /// Invalid if specified for AUTOMATIC_GAIN_CONTROL, AUTOMATIC_GAIN_LIMITER, CONNECTION_POINT, |
| /// DAI_INTERCONNECT, DELAY, MUTE, RING_BUFFER or SAMPLE_RATE_CONVERSION elements. |
| 1: type_specific SettableTypeSpecificElementState; |
| |
| /// If included, an opaque object of octets for conveying vendor-specific information from |
| /// a client to the audio driver. |
| /// This can be used with any element type, not just VENDOR_SPECIFIC elements. |
| /// |
| /// Optional. |
| @available(added=27) |
| 2: vendor_specific_data vector<uint8>:MAX_BYTES_ELEMENT_VENDOR_SPECIFIC; |
| |
| /// Whether to start or stop this processing element. |
| /// A stopped processing element does not provide its abstracted functionality. |
| /// Specifically, no audio data flows through a stopped element. |
| /// |
| /// If the corresponding `Element` returned `can_stop` equals to `false`, then this field must |
| /// not be set to `false` -- `SetElementState` will return ZX_ERR_INVALID_ARGS in that case. |
| /// If not set, then the element's previous `started` state will be unchanged. |
| /// |
| /// Optional. |
| 3: started bool; |
| |
| /// Whether to bypass this processing element. |
| /// A bypassed element does not affect the flow of audio through the topology. |
| /// Specifically, audio flows through a bypassed element, without change. |
| /// |
| /// If the corresponding `Element` omits `can_bypass` or sets it to `false`, then this field |
| /// must not be set to `true`. If this occurs, `SetElementState` will fail and return error |
| /// `ZX_ERR_INVALID_ARGS`. |
| /// If not set, then the element's previous `bypassed` state will be unchanged. |
| /// |
| /// Optional. |
| 4: bypassed bool; |
| }; |
| |
| /// Edge pairs between processing elements, used to specify how audio flows sequentially through |
| /// a collection of processing element arrangements. |
| type EdgePair = struct { |
| processing_element_id_from ElementId; |
| processing_element_id_to ElementId; |
| }; |
| |
| /// A `Topology` specifies how processing elements are arranged within the hardware. |
| type Topology = table { |
| /// Unique ID for this topology. The scope of this id is only within the `SignalProcessing` |
| /// protocol lifespan, i.e. until the channel associated with the protocol is closed. |
| /// |
| /// Required. |
| 1: id TopologyId; |
| |
| /// Vector of processing elements edge pairs that specify connections between elements. |
| /// Processing elements are connected by edge pairs, to form multi-element pipelines. |
| /// A Topology can contain more than one distinct pipeline: the Topology need not be a single |
| /// interconnected sequence (e.g. Topology A->B->C D->E->F is valid). |
| /// |
| /// To define multiple possible configurations where one possibility can be selected by the |
| /// client, return multiple `Topology` entries in `GetTopologies`. |
| /// |
| /// If a device does support multiple Topology entries, then each specific Topology is not |
| /// required to include every Element. However, every element must be included in at least one |
| /// Topology. |
| /// |
| /// Within each Topology, every sequence of connected elements must begin with an element |
| /// of type DAI_INTERCONNECT or RING_BUFFER, and must end with an element of type |
| /// DAI_INTERCONNECT or RING_BUFFER. |
| /// |
| /// An DAI_INTERCONNECT element is _permitted_ to be the endpoint for an element sequence, but a |
| /// RING_BUFFER is _required_ to be one. If a certain RING_BUFFER element is listed in an |
| /// EdgeList entry as a `processing_element_id_from`, then within that Topology this same |
| /// element must NOT be listed in another EdgeList entry as a `processing_element_id_to` (and |
| /// vice versa). |
| /// |
| /// As a special case, a DAI_INTERCONNECT element can refer to itself; i.e. an EdgePair may |
| /// contain `processing_element_id_from` and `processing_element_id_to` values that are equal. |
| /// However, this element must not _also_ connect to _other_ elements within the Topology (this |
| /// EdgePair must be the only one in `processing_elements_edge_pairs` to mention that element). |
| /// |
| /// Required. Must contain at least one entry. |
| 2: processing_elements_edge_pairs vector<EdgePair>:MAX_COUNT_PROCESSING_ELEMENTS_EDGE_PAIRS; |
| }; |
| |
| /// This protocol is required for Composite audio drivers, and unsupported for other audio driver |
| /// types (Codec, Dai, StreamConfig). |
| /// |
| /// For an overview see |
| /// [[Signal Processing Interface]](https://fuchsia.dev/fuchsia-src/concepts/drivers/driver_architectures/audio_drivers/audio_signal_processing). |
| open protocol SignalProcessing { |
| /// Exposes read-only signal processing properties. |
| compose Reader; |
| |
| /// Sets the currently active topology by specifying a `topology_id`, which matches to an entry |
| /// in the vector returned by `GetTopologies`. |
| /// The currently active topology is communicated by `WatchTopology` responses. To change which |
| /// topology is active, a client uses `SetTopology`. |
| /// |
| /// If `GetTopologies` returns only one `Topology`, `SetTopology` is optional and has no effect. |
| /// |
| /// This call will fail and return `ZX_ERR_INVALID_ARGS` if the specified `topology_id` is not |
| /// found within the`topologies` returned by `GetTopologies`. |
| /// |
| /// `SetTopology` may be called before or after non-`SignalProcessing` protocol calls. |
| /// If called after non-`SignalProcessing` protocol calls, then `SetTopology` may return |
| /// `ZX_ERR_BAD_STATE` to indicate that the operation can not proceed without renegotiation of |
| /// the driver state. See `SetElementState` for further discussion. |
| strict SetTopology(struct { |
| topology_id TopologyId; |
| }) -> () error zx.Status; |
| |
| /// Controls the processing element specified by `processing_element_id`, a unique ElementId |
| /// returned by `GetElements`. |
| /// The `state` specified in calls to `SetElementState` is a `SettableElementState`. This is a |
| /// subset of `ElementState` because some fields returned by `WatchElementState` (e.g. `latency` |
| /// or `plug_state`) can only be observed (not set) by the client. |
| /// |
| /// Returns `ZX_ERR_INVALID_ARGS` if `processing_element_id` does not match a known ElementId |
| /// returned by `GetElements`, or if `state` is not valid for the element. This entails any |
| /// violation of the rules specified in this protocol. |
| /// |
| /// Examples: |
| /// `state` specifies that an element should be stopped or bypassed, but the corresponding |
| /// element does not specify (or explicitly set to false) `can_stop` or `can_bypass`. |
| /// `state` includes a `type_specific` entry, but that `SettableTypeSpecificElementState` does |
| /// not match the `ElementType` of the element corresponding to `processing_element_id`. |
| /// `state` changes an `EqualizerBandState` for an `EQUALIZER` element (so far so good), but |
| /// specifies a change to `frequency` when this element did not set `CAN_CONTROL_FREQUENCY` |
| /// in its `supported_controls`. |
| /// `state` specifies a `GainElementState` for a `GAIN` element with a `gain` value that is |
| /// -infinity, NAN, or outside the Element's stated [`min_gain`, `max_gain`] range. |
| /// |
| /// Callers may intersperse method calls to the `SignalProcessing` protocol with calls to other |
| /// driver protocols. Some non-`SignalProcessing` configuration changes may require a |
| /// renegotiation of the driver state before certain elements can receive a `SetElementState`. |
| /// For example, if a `DaiFormat` is changed, then `SetElementState` changing an `AGL` element's |
| /// parameters may not require renegotiation of driver state because changing gain parameters |
| /// usually does not change the set of supported audio formats. |
| /// By contrast, following the same `DaiFormat` change, before `SetElementState` can be called |
| /// on a `CONNECTION_POINT` element, the driver state may need to be reestablished because the |
| /// format change may invalidate the set of supported formats returned in a previous |
| /// `GetDaiFormats` protocol call for another part of the Topology. |
| /// |
| /// It is the driver's job to determine when renegotiation is required. When this is needed, |
| /// the related `SetElementState` call must return `ZX_ERR_BAD_STATE` and the client must |
| /// close the protocol channel entirely, such that the protocol negotiations are started over. |
| /// The client then must re-invoke the `SetElementState` call that returned |
| /// `ZX_ERR_BAD_STATE` before any non-`SignalProcessing` protocol calls. |
| strict SetElementState(struct { |
| processing_element_id ElementId; |
| |
| state SettableElementState; |
| }) -> () error zx.Status; |
| }; |
| |
| /// This protocol is required for Composite audio drivers, and unsupported for other audio driver |
| /// types (Codec, Dai, StreamConfig). |
| /// |
| /// For an overview see |
| /// [[Signal Processing Interface]](https://fuchsia.dev/fuchsia-src/concepts/drivers/driver_architectures/audio_drivers/audio_signal_processing). |
| open protocol Reader { |
| /// Returns a vector of supported processing elements. |
| /// This vector must include one or more processing elements. |
| strict GetElements() -> (struct { |
| processing_elements vector<Element>:MAX_COUNT_PROCESSING_ELEMENTS; |
| }) error zx.Status; |
| |
| /// Get the processing element state via a hanging get. |
| /// For a given `processing_element_id`, the driver will immediately reply to the first |
| /// `WatchElementState` sent by the client. The driver will not respond to subsequent client |
| /// `WatchElementState` calls for that `processing_element_id` until any portion of the |
| /// `ElementState` has changed from what was most recently reported for that element. |
| /// |
| /// The driver will close the protocol channel with an error of `ZX_ERR_INVALID_ARGS`, if |
| /// `processing_element_id` does not match an ElementId returned by `GetElements`. |
| /// |
| /// The driver will close the protocol channel with an error of `ZX_ERR_BAD_STATE`, if this |
| /// method is called again while there is already a pending `WatchElementState` for this client |
| /// and `processing_element_id`. |
| strict WatchElementState(struct { |
| processing_element_id ElementId; |
| }) -> (struct { |
| state ElementState; |
| }); |
| |
| /// Returns a vector of supported topologies. |
| /// This vector must include one or more topologies. |
| /// If more than one topology is returned, then the client may select any topology from the |
| /// list by calling `SetTopology`. |
| /// If only one topology is returned, `SetTopology` can still be called but causes no change. |
| /// |
| /// Each Element must be included in at least one Topology, but need not be included in every |
| /// Topology. |
| strict GetTopologies() -> (struct { |
| topologies vector<Topology>:MAX_COUNT_TOPOLOGIES; |
| }) error zx.Status; |
| |
| /// Get the current topology via a hanging get. |
| /// The driver will immediately reply to the first `WatchTopology` sent by each client. |
| /// The driver will not respond to subsequent `WatchTopology` calls from that client until the |
| /// signal processing topology changes; this occurs as a result of a `SetTopology` call. |
| /// |
| /// The driver will close the protocol channel with an error of `ZX_ERR_BAD_STATE`, if this |
| /// method is called again while there is already a pending `WatchTopology` for this client. |
| @available(added=27) |
| flexible WatchTopology() -> (struct { |
| topology_id TopologyId; |
| }); |
| }; |