|  | // Copyright 2019 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.virtualaudio; | 
|  |  | 
|  | using zx; | 
|  |  | 
|  | const uint32 MAX_UI_STRING_SIZE = 256; | 
|  |  | 
|  | // fuchsia.virtualaudio.Forwarder | 
|  | // | 
|  |  | 
|  | /// Using this Simple Layout (C-bound) protocol, an intermediary (such as the | 
|  | /// virtual audio service) forwards FIDL protocol requests to the virtual audio | 
|  | /// driver, which enables clients to use more full-featured (C++ based) bindings | 
|  | /// with this driver -- specifically the Control, Input and Output protocols. | 
|  | [ForDeprecatedCBindings] | 
|  | protocol Forwarder { | 
|  | SendControl(request<Control> control); | 
|  | SendInput(request<Input> input); | 
|  | SendOutput(request<Output> output); | 
|  | }; | 
|  |  | 
|  | // fuchsia.virtualaudio.Control | 
|  | // | 
|  |  | 
|  | /// This protocol provides the caller a high-level ON/OFF switch for overall | 
|  | /// virtual audio functionality at the system level. When virtualaudio is | 
|  | /// disabled, device configurations can be created and changed, but no devices | 
|  | /// can be added. When virtualaudio is enabled, device configurations can again | 
|  | /// be converted into devices by calling `Add()`. | 
|  | [Discoverable] | 
|  | protocol Control { | 
|  | /// Allow inputs and outputs to be activated, but do not automatically | 
|  | /// reactivate those previously deactivated by `Disable()`. Does not affect | 
|  | /// existing Configs. By default, virtualaudio is enabled on system startup. | 
|  | /// This method's callback can be used as a mechanism to synchronize with | 
|  | /// other asynchronous in-flight virtualaudio FIDL operations. | 
|  | Enable() -> (); | 
|  |  | 
|  | /// Deactivate all active inputs/outputs; disallow subsequent activations. | 
|  | /// This method's callback can be used as a mechanism to synchronize with | 
|  | /// other asynchronous in-flight virtualaudio FIDL operations. | 
|  | Disable() -> (); | 
|  |  | 
|  | /// Return the number of active input and output virtual devices. | 
|  | /// This method's callback can be used as a mechanism to synchronize with | 
|  | /// other asynchronous in-flight virtualaudio FIDL operations. | 
|  | GetNumDevices() -> (uint32 num_input_devices, | 
|  | uint32 num_output_devices); | 
|  | }; | 
|  |  | 
|  | // | 
|  | // The Input and Output protocols closely correspond to the capabilities | 
|  | // exposed by the Audio Driver Streaming Interface, fully documented at | 
|  | // driver-interfaces/audio.md and declared at device/audio.h. | 
|  | // | 
|  |  | 
|  | // fuchsia.virtualaudio.Input | 
|  | // | 
|  | /// This protocol represents an active virtual audio input device. It inherits | 
|  | /// the parent protocols Device and Configuration. This protocol, as well as the | 
|  | /// contents of Device, represent actions that can be taken by an active input | 
|  | /// device -- actions that should be immediately detected and reacted upon by | 
|  | /// the audio subsystem. | 
|  | [Discoverable] | 
|  | protocol Input { | 
|  | compose Device; | 
|  |  | 
|  | // TODO(mpuryear): `SetCaptureSignal()` -- indicate what to "capture". | 
|  | // E.g. sinusoid, constant, square, ramp, saw, noise. Can differ by channel. | 
|  |  | 
|  | // TODO(mpuryear): `PairWithOutput()` -- client passes an output (server) | 
|  | // binding; input device returns as captured data the streamed output data. | 
|  | }; | 
|  |  | 
|  | // fuchsia.virtualaudio.Output | 
|  | // | 
|  |  | 
|  | /// This protocol represents an active virtual audio output device. It inherits | 
|  | /// the parent protocols Device and Configuration. This protocol, as well as the | 
|  | /// contents of Device, represent actions that can be taken by an active output | 
|  | /// device -- actions that should be immediately detected and reacted upon by | 
|  | /// the audio subsystem. | 
|  | [Discoverable] | 
|  | protocol Output { | 
|  | compose Device; | 
|  |  | 
|  | // TODO(mpuryear): any render-specific configuration or runtime triggers. | 
|  | }; | 
|  |  | 
|  | /// This protocol represents the base functionality of active Input and Output | 
|  | /// audio devices -- methods that are common to both protocols. This protocol, | 
|  | /// as well as the contents of Output and Input, represent actions that can be | 
|  | /// taken by an active device -- actions that should be immediately detected and | 
|  | /// reacted upon by the audio subsystem. | 
|  | protocol Device { | 
|  | compose Configuration; | 
|  |  | 
|  | /// Activate (`DdkAdd`) the virtual audio device as currently configured. A | 
|  | /// device node will be published and detected by the AudioDeviceManager, | 
|  | /// and a driver for the virtual device will be loaded and queried. Device | 
|  | /// arrivals are exposed to clients by the | 
|  | /// `fuchsia.media.AudioDeviceEnumerator` protocol, in `GetDevices()` and | 
|  | /// the `->OnDeviceAdded()` event. | 
|  | Add(); | 
|  |  | 
|  | /// Deactivate (`DdkRemove`) the active virtual audio device, but retain its | 
|  | /// configuration for future activation. The driver for the virtual device | 
|  | /// will be unloaded, and the device node closed. Device removals are | 
|  | /// exposed to clients by `fuchsia.media.AudioDeviceEnumerator`, in | 
|  | /// `GetDevices()` and `->OnDeviceRemoved()`. | 
|  | Remove(); | 
|  |  | 
|  | /// Return the format selected by the client, when that client issued an | 
|  | /// `AUDIO_STREAM_CMD_SET_FORMAT` command. This can only occur after a | 
|  | /// device has been added, and is only allowed to occur before the device's | 
|  | /// ring buffer has been returned (i.e., before an | 
|  | /// `AUDIO_RB_CMD_GET_BUFFER` command). | 
|  | GetFormat() -> (uint32 frames_per_second, uint32 sample_format, | 
|  | uint32 num_channels, zx.duration external_delay); | 
|  |  | 
|  | /// Notify all subscribed listeners when the above format is set or changed. | 
|  | -> OnSetFormat(uint32 frames_per_second, uint32 sample_format, | 
|  | uint32 num_channels, zx.duration external_delay); | 
|  |  | 
|  | /// Return the current gain state for this device. After a device has been | 
|  | /// added, a client can call this at any time -- even before the ring buffer | 
|  | /// has been established, or before the format has been set. | 
|  | GetGain() -> (bool current_mute, bool current_agc, float32 current_gain_db); | 
|  |  | 
|  | /// Notify all subscribed listeners when the above gain is set or changed. | 
|  | -> OnSetGain(bool current_mute, bool current_agc, float32 current_gain_db); | 
|  |  | 
|  | /// Return details about the ring buffer that was established in response | 
|  | /// to a client `AUDIO_RB_CMD_GET_BUFFER` command. This will only occur | 
|  | /// after the client sets the format and retrieves other driver information. | 
|  | GetBuffer() -> (zx.handle:VMO ring_buffer, uint32 num_ring_buffer_frames, | 
|  | uint32 notifications_per_ring); | 
|  |  | 
|  | /// Notify all subscribed listeners when the above buffer has been created. | 
|  | -> OnBufferCreated(zx.handle:VMO ring_buffer, uint32 num_ring_buffer_frames, | 
|  | uint32 notifications_per_ring); | 
|  |  | 
|  | /// Override the position notification frequency set by AudioCore for this | 
|  | /// stream, with the given value. Although this method can be called at any | 
|  | /// time (including before this Input|Output is added, or after it is | 
|  | /// started), logically it makes most sense to call this immediately after | 
|  | /// receiving details about the just-created ring buffer, via `GetBuffer` or | 
|  | /// the `->OnBufferCreated` event. | 
|  | SetNotificationFrequency(uint32 notifications_per_ring); | 
|  |  | 
|  | /// Notify all subscribed listeners when the device is commanded to Start | 
|  | /// streaming. This can only occur after a device is fully configured | 
|  | /// (format is set; ring buffer is established and fetched). | 
|  | -> OnStart(zx.time start_time); | 
|  |  | 
|  | /// Notify all subscribed listeners when the device is commanded to Stop | 
|  | /// streaming. This can only occur when the device is already Started. Stop | 
|  | /// returns the device to a fully-configured state. Upon this command, the | 
|  | /// already-set format and ring buffer are retained without change, but | 
|  | /// position will re-begin at 0, if the device is again Started. | 
|  | -> OnStop(zx.time stop_time, uint32 ring_position); | 
|  |  | 
|  | /// Return the current position (in bytes) within the ring buffer, along | 
|  | /// with the time (per MONOTONIC clock) that corresponds with that position. | 
|  | /// This can only be called after the ring buffer is established. If the | 
|  | /// device has not yet Started streaming, then zero will always be returned. | 
|  | GetPosition() -> (zx.time monotonic_time, uint32 ring_position); | 
|  |  | 
|  | /// Notify all subscribed listeners, when any `AUDIO_RB_POSITION_NOTIFY` | 
|  | /// position notification is issued by the driver. The frequency of these | 
|  | /// per-stream notifications is set by AudioCore, reported to VAD clients | 
|  | /// via `GetBuffer` or the `->OnBufferCreated` event. VirtualAudioDevice | 
|  | /// clients can enable an alternate notification frequency for a given | 
|  | /// stream by calling `SetNotificationFrequency`. As with a direct call to | 
|  | /// `GetPosition`, the returned parameters are the current position (in | 
|  | /// bytes) in the ring buffer, as well as the time (per MONOTONIC clock) | 
|  | /// that corresponds with that ring buffer position. | 
|  | -> OnPositionNotify(zx.time monotonic_time, uint32 ring_position); | 
|  |  | 
|  | /// Hot-plug or hot-unplug an active virtual device, at the specified time. | 
|  | /// For devices marked as capable of asynchronously notifying the system of | 
|  | /// plug changes, the driver will now send the values using | 
|  | /// `AUDIO_STREAM_PLUG_DETECT_NOTIFY`. Else, values will be reflected when | 
|  | /// the driver is next sent an `AUDIO_STREAM_CMD_PLUG_DETECT` command. This | 
|  | /// information is used by the system when determining which device is | 
|  | /// default. This, in turn, is exposed to clients by the | 
|  | /// `fuchsia.media.AudioDeviceEnumerator` protocol: in `GetDevices()`, | 
|  | /// `GetDefaultInputDevice()`/`GetDefaultOutputDevice()` and the | 
|  | /// `->OnDefaultDeviceChanged()` event. | 
|  | ChangePlugState(zx.time plug_change_time, bool plugged); | 
|  |  | 
|  | /// Immediately change the virtual device's clock rate, as expressed in the | 
|  | /// timing and content of position notifications the driver emits. | 
|  | /// 'ppm_monotonic' cannot exceed [-1000,+1000]. Each rate change in rate is | 
|  | /// standalone; i.e. successive rate changes are not cumulative. | 
|  | AdjustClockRate(int32 ppm_from_monotonic); | 
|  | }; | 
|  |  | 
|  | /// This protocol is conceptually a base protocol to Device. It exposes the | 
|  | /// methods used to specify the properties of a virtual audio device (its | 
|  | /// configuration), before the virtual device is instantiated by the call to | 
|  | /// `Add()`. Although the non-Add methods on this protocol can be called after | 
|  | /// calling `Add()` (i.e., after Configuration has been converted into active | 
|  | /// Device), this only changes how future Devices will be created; it has no | 
|  | /// effect on already-created Devices. | 
|  | protocol Configuration { | 
|  | /// Set the virtual audio device's name. This corresponds to the value | 
|  | /// associated with the device node for this virtual device. This must be | 
|  | /// called before calling `Add()`, or after `Remove()`. | 
|  | SetDeviceName(string device_name); | 
|  |  | 
|  | /// Set the virtual audio device's manufacturer name. Must be called before | 
|  | /// `Add()` or after `Remove()`. After device activation, the driver returns | 
|  | /// this value in response to an `AUDIO_STREAM_CMD_GET_STRING` command of ID | 
|  | /// `AUDIO_STREAM_STR_ID_MANUFACTURER`. This is exposed to clients via the | 
|  | /// `fuchsia.media.AudioDeviceEnumerator` protocol, in an `AudioDeviceInfo` | 
|  | /// struct returned from `GetDevices()` and the `->OnDeviceAdded()` event. | 
|  | SetManufacturer(string:MAX_UI_STRING_SIZE manufacturer_name); | 
|  |  | 
|  | /// Set the virtual audio device's product name. This must be called before | 
|  | /// `Add()` or after `Remove()`. After device activation, the driver returns | 
|  | /// this value in response to an `AUDIO_STREAM_CMD_GET_STRING` command of ID | 
|  | /// `AUDIO_STREAM_STR_ID_PRODUCT`. This name is exposed to clients via the | 
|  | /// `fuchsia.media.AudioDeviceEnumerator` protocol, in an `AudioDeviceInfo` | 
|  | /// struct returned from `GetDevices()` and the `->OnDeviceAdded()` event. | 
|  | SetProduct(string:MAX_UI_STRING_SIZE product_name); | 
|  |  | 
|  | /// Set the virtual audio device's unique ID, a 16-character string. This | 
|  | /// must be called before calling `Add()`, or after `Remove()`. Once the | 
|  | /// device is activated, this value is returned by the driver in response | 
|  | /// to an `AUDIO_STREAM_CMD_GET_UNIQUE_ID` command. This value is exposed | 
|  | /// to clients by the `fuchsia.media.AudioDeviceEnumerator` protocol: | 
|  | /// returned in an `AudioDeviceInfo` struct by `GetDevices()` or | 
|  | /// `->OnDeviceAdded()`. | 
|  | SetUniqueId(array<uint8>:16 unique_id); | 
|  |  | 
|  | /// Add a supported format range for this audio device. This must be called | 
|  | /// before calling `Add()`, or after `Remove()`. Once the device is | 
|  | /// activated, format ranges are returned by the driver in response to an | 
|  | /// `AUDIO_STREAM_CMD_GET_FORMATS` command. sample_format_flags is of type | 
|  | /// audio_sample_format_t, and rate_family_flags is a bit field of possible | 
|  | /// constants beginning with `ASF_RANGE_FLAG_FPS_`. See audio.h for details. | 
|  | AddFormatRange(uint32 sample_format_flags, uint32 min_frame_rate, | 
|  | uint32 max_frame_rate, uint8 min_channels, | 
|  | uint8 max_channels, uint16 rate_family_flags); | 
|  |  | 
|  | /// Remove the minimal format range that is added by default to all | 
|  | /// configurations. As with `AddFormatRange()`, this method must be called | 
|  | /// before calling `Add()`, or after `Remove()`. | 
|  | ClearFormatRanges(); | 
|  |  | 
|  | [Transitional] | 
|  | SetClockDomain(int32 clock_domain); | 
|  |  | 
|  | /// Set properties for this virtual device's clock. These include the clock | 
|  | /// domain and the initial rate difference (in ppm) between its clock and | 
|  | /// the local monotonic clock. The clock domain is an int32 provided by the | 
|  | /// clock tree to an audio driver; special values for CLOCK_DOMAIN_MONOTONIC | 
|  | /// [0], and CLOCK_DOMAIN_EXTERNAL [-1] (not locally controllable) are | 
|  | /// defined in fuchsia.hardware.audio/stream.fidl. Other than -1, any | 
|  | /// negative value for clock_domain is invalid and will be ignored. A | 
|  | /// non-zero value for 'initial_rate_adjustment_ppm' is only valid (and will | 
|  | /// only be accepted) if the clock_domain is NOT equal to 0. This method | 
|  | /// must be called before calling `Add()`, or after `Remove()`. Once the | 
|  | /// device is activated, the clock_domain value is returned by the driver in | 
|  | /// response to an `AUDIO_STREAM_CMD_GET_CLOCK_DOMAIN` command, and the | 
|  | /// clock rate is expressed in the timing, and values, of position | 
|  | /// notifications. Neither clock domain nor position notifications are | 
|  | /// directly exposed to clients by public APIs, but these are both used in | 
|  | /// AudioCore's device (or more accurately, domain) clock recovery. | 
|  | SetClockProperties(int32 clock_domain, int32 initial_rate_adjustment_ppm); | 
|  |  | 
|  | /// Set the virtual audio device's fifo depth, in bytes. This must be called | 
|  | /// before calling `Add()`, or after `Remove()`. Once the device is | 
|  | /// activated, the depth of its FIFO is returned by the driver in response | 
|  | /// to an `AUDIO_RB_CMD_GET_FIFO_DEPTH` command on the ring buffer channel. | 
|  | SetFifoDepth(uint32 fifo_depth_bytes); | 
|  |  | 
|  | /// Set the virtual audio device's external delay, in nanoseconds. This must | 
|  | /// be called before calling `Add()`, or after `Remove()`. Once the device | 
|  | /// is activated, this value is returned by the driver in response to an | 
|  | /// `AUDIO_STREAM_CMD_SET_FORMAT` command. | 
|  | // | 
|  | // Treating this as a static property is inadequate, considering that the | 
|  | // delay is intended to be calculated from the format that was just set. | 
|  | SetExternalDelay(zx.duration external_delay); | 
|  |  | 
|  | /// Set restrictions for the device ring buffer. This must be called before | 
|  | /// calling `Add()`, or after `Remove()`. Once the device is activated, the | 
|  | /// ring buffer and its size are returned by the driver in response to an | 
|  | /// `AUDIO_RB_CMD_GET_BUFFER` command on the ring buffer channel. | 
|  | /// Note: both min_frames and max_frames must be multiples of modulo_frames. | 
|  | SetRingBufferRestrictions(uint32 min_frames, uint32 max_frames, | 
|  | uint32 modulo_frames); | 
|  |  | 
|  | /// Set gain properties for this virtual device. This must be called before | 
|  | /// calling `Add()`, or after `Remove()`. Once the device is activated, gain | 
|  | /// information is returned by the driver in an | 
|  | /// `audio_stream_cmd_get_gain_resp` struct, in response to an | 
|  | /// `AUDIO_STREAM_CMD_GET_GAIN` command. This information is exposed to | 
|  | /// clients by the `fuchsia.media.AudioDeviceEnumerator` protocol: returned | 
|  | /// in an `AudioGainInfo` struct by `GetDeviceGain()` and the | 
|  | /// `->OnDeviceGainChanged()` event. | 
|  | SetGainProperties(float32 min_gain_db, float32 max_gain_db, | 
|  | float32 gain_step_db, float32 current_gain_db, | 
|  | bool can_mute, bool current_mute, | 
|  | bool can_agc, bool current_agc); | 
|  |  | 
|  | /// Set plug properties for this virtual device. This must be called before | 
|  | /// calling `Add()`, or after `Remove()`. Once the device is activated, plug | 
|  | /// information is returned by the driver in response to an | 
|  | /// `AUDIO_STREAM_CMD_PLUG_DETECT` command. This information is used by the | 
|  | /// system when determining which device is default. This in turn is exposed | 
|  | /// to clients by the `fuchsia.media.AudioDeviceEnumerator` protocol: in | 
|  | /// `GetDevices()`, `GetDefaultInputDevice()`/`GetDefaultOutputDevice()` and | 
|  | /// the `->OnDefaultDeviceChanged()` event. | 
|  | SetPlugProperties(zx.time plug_change_time, bool plugged, bool hardwired, | 
|  | bool can_notify); | 
|  |  | 
|  | /// Return a configuration to its default settings. This call has no effect | 
|  | /// on active devices. In other words, it must be called before calling | 
|  | /// `Add()`, or after `Remove()`. | 
|  | ResetConfiguration(); | 
|  | }; |