| /* |
| * SPDX-FileCopyrightText: Copyright (c) 2026 Zhenjiang Zhang |
| * SPDX-FileCopyrightText: Copyright (c) 2026 HiFiPhile (Zixun LI) |
| * SPDX-License-Identifier: MIT |
| * |
| * This file is part of the TinyUSB stack. |
| */ |
| |
| #ifndef TUSB_AUDIO_HOST_H_ |
| #define TUSB_AUDIO_HOST_H_ |
| |
| #include "audio.h" |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| //--------------------------------------------------------------------+ |
| // Class Driver Configuration |
| //--------------------------------------------------------------------+ |
| |
| // Audio Class protocol versions compiled into the host driver. Multiple |
| // versions can be enabled so UAC1 and UAC2 devices can be mounted together. |
| #define TUH_AUDIO_PROTOCOL_UAC1 TU_BIT(0) |
| #define TUH_AUDIO_PROTOCOL_UAC2 TU_BIT(1) |
| |
| #ifndef CFG_TUH_AUDIO_PROTOCOLS |
| #define CFG_TUH_AUDIO_PROTOCOLS TUH_AUDIO_PROTOCOL_UAC1 |
| #endif |
| |
| #if !(CFG_TUH_AUDIO_PROTOCOLS & (TUH_AUDIO_PROTOCOL_UAC1 | TUH_AUDIO_PROTOCOL_UAC2)) |
| #error CFG_TUH_AUDIO_PROTOCOLS must enable UAC1 and/or UAC2 |
| #endif |
| |
| #if CFG_TUH_AUDIO_PROTOCOLS & ~(TUH_AUDIO_PROTOCOL_UAC1 | TUH_AUDIO_PROTOCOL_UAC2) |
| #error CFG_TUH_AUDIO_PROTOCOLS contains an unsupported protocol bit |
| #endif |
| |
| // Maximum number of Audio devices |
| #ifndef CFG_TUH_AUDIO_MAX |
| #define CFG_TUH_AUDIO_MAX 1 |
| #endif |
| // Maximum discrete sampling frequencies retained per rate source. UAC1 uses |
| // one rate source per alternate setting; UAC2 alternate settings may share a |
| // Clock Source. |
| #ifndef CFG_TUH_AUDIO_MAX_SAM_FREQ |
| #define CFG_TUH_AUDIO_MAX_SAM_FREQ 5 |
| #endif |
| // Maximum supported nonzero-bandwidth Audio Streaming alternate settings per |
| // logical stream. |
| #ifndef CFG_TUH_AUDIO_MAX_AS |
| #define CFG_TUH_AUDIO_MAX_AS 4 |
| #endif |
| |
| // Maximum size of one capture (IN) isochronous transfer the driver submits. |
| // Configurations needing a larger per-poll-interval packet are rejected. |
| // 256 covers 2-ch 48 kHz S16_LE (192 B) and common endpoint padding (208 B). |
| #ifndef CFG_TUH_AUDIO_EPIN_BUFSIZE |
| #define CFG_TUH_AUDIO_EPIN_BUFSIZE 256 |
| #endif |
| |
| // Maximum size of one playback (OUT) isochronous transfer the driver submits. |
| // Configurations needing a larger per-poll-interval packet are rejected. |
| #ifndef CFG_TUH_AUDIO_EPOUT_BUFSIZE |
| #define CFG_TUH_AUDIO_EPOUT_BUFSIZE 256 |
| #endif |
| |
| // Depth in bytes of the per-stream data FIFO. The FIFO decouples the |
| // application's read/write calls from the endpoint's isochronous polling cadence |
| // and absorbs rate differences. Capture overwrites the oldest frames when full. |
| // 1024 bytes hold 4 default (256 B) packets. |
| #ifndef CFG_TUH_AUDIO_STREAM_BUFSIZE |
| #define CFG_TUH_AUDIO_STREAM_BUFSIZE 1024 |
| #endif |
| |
| //--------------------------------------------------------------------+ |
| // Types |
| //--------------------------------------------------------------------+ |
| |
| // Fixed transfer direction of a logical stream. |
| typedef enum { |
| TUH_AUDIO_STREAM_PLAYBACK = 0, // Host -> Device (OUT) |
| TUH_AUDIO_STREAM_CAPTURE = 1, // Device -> Host (IN) |
| TUH_AUDIO_STREAM_DIRECTION_COUNT |
| } tuh_audio_direction_t; |
| |
| // Asynchronous stream operation and transport events. |
| typedef enum { |
| TUH_AUDIO_EVENT_START_COMPLETE = 0, |
| TUH_AUDIO_EVENT_STOP_COMPLETE, |
| TUH_AUDIO_EVENT_XFER_FAILED |
| } tuh_audio_event_t; |
| |
| // Discrete Type-I PCM sample format. UAC1 requires bSamFreqType > 0; UAC2 |
| // configurations are built from the directly connected Clock Source RANGE. |
| typedef enum { |
| TUH_AUDIO_FORMAT_S8 = 0, // signed 8-bit |
| TUH_AUDIO_FORMAT_S16_LE, // signed 16-bit little-endian |
| TUH_AUDIO_FORMAT_S24_3LE, // signed 24-bit packed in 3 bytes, LE |
| TUH_AUDIO_FORMAT_S24_LE, // signed 24-bit in 32-bit container, LE |
| TUH_AUDIO_FORMAT_S32_LE, // signed 32-bit little-endian |
| TUH_AUDIO_FORMAT_COUNT |
| } tuh_audio_format_t; |
| |
| // One complete supported discrete configuration tuple. |
| // Each entry is a full (format, sample_rate, channels) combination, |
| // avoiding invalid mixes between independent format/rate/channel lists. |
| // dir is constant for all configs of a given (dev_idx, stream_idx) and |
| // equals the result of tuh_audio_stream_direction(). |
| typedef struct { |
| uint32_t sample_rate; |
| tuh_audio_direction_t dir; |
| tuh_audio_format_t format; |
| uint8_t channels; |
| } tuh_audio_stream_config_t; |
| |
| // Feature Unit channel zero selects the master channel. |
| #define TUH_AUDIO_CHANNEL_MASTER 0 |
| |
| // Volume values are signed 1/256 dB. INT16_MIN represents silence. |
| #define TUH_AUDIO_VOLUME_SILENCE INT16_MIN |
| |
| // One continuous volume range. This matches UAC1 MIN/MAX/RES and the common |
| // UAC2 RANGE response containing one subrange. |
| typedef struct { |
| int16_t min; |
| int16_t max; |
| uint16_t res; |
| } tuh_audio_volume_range_t; |
| |
| // Audio Control descriptors reported during enumeration. Descriptor pointers |
| // are valid only for the duration of tuh_audio_descriptor_cb(). |
| typedef struct { |
| const tusb_desc_interface_t *desc_audio_control; |
| const uint8_t *desc_cs_audio_control; |
| uint16_t desc_cs_audio_control_len; |
| } tuh_audio_descriptor_cb_t; |
| |
| //--------------------------------------------------------------------+ |
| // Stream Enumeration |
| //--------------------------------------------------------------------+ |
| |
| // Number of logical audio streams exposed by one mounted device. The |
| // application iterates stream indices [0, tuh_audio_stream_count()) and |
| // inspects each with tuh_audio_stream_exists()/tuh_audio_stream_direction(). |
| uint8_t tuh_audio_stream_count(uint8_t dev_idx); |
| |
| // True if (dev_idx, stream_idx) identifies an existing stream. |
| bool tuh_audio_stream_exists(uint8_t dev_idx, uint8_t stream_idx); |
| |
| // Fixed transfer direction of the stream. |
| tuh_audio_direction_t tuh_audio_stream_direction(uint8_t dev_idx, uint8_t stream_idx); |
| |
| //--------------------------------------------------------------------+ |
| // Configuration Enumeration |
| //--------------------------------------------------------------------+ |
| |
| // Number of supported discrete configurations of the stream. |
| uint8_t tuh_audio_config_count(uint8_t dev_idx, uint8_t stream_idx); |
| |
| // Active configuration index of the stream, or TUSB_INDEX_INVALID_8 if none. |
| uint8_t tuh_audio_active_config(uint8_t dev_idx, uint8_t stream_idx); |
| |
| // Retrieve one discrete configuration tuple into *config. |
| bool tuh_audio_config_get(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_idx, tuh_audio_stream_config_t *config); |
| |
| //--------------------------------------------------------------------+ |
| // Configuration (ALSA hw_params analogue) |
| //--------------------------------------------------------------------+ |
| |
| // Synchronously configure the stream with the discrete configuration identified by |
| // config_idx. The driver: |
| // 1. resolves the AS interface and alternate setting, |
| // 2. initializes the FIFO and packet scheduler, |
| // 3. opens / reconfigures only the selected endpoint. |
| bool tuh_audio_configure(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_idx); |
| |
| //--------------------------------------------------------------------+ |
| // Stream Control / Frame-based Data |
| //--------------------------------------------------------------------+ |
| |
| // Start transferring data with the configuration selected by configure(). |
| // UAC1 activates the alternate setting before setting an endpoint frequency; |
| // UAC2 sets a writable Clock Source before activating the alternate setting. |
| // Startup is asynchronous: true means that the first request was submitted. |
| // Completion is reported through tuh_audio_event_cb(); no event is emitted |
| // when this function returns false. |
| bool tuh_audio_start(uint8_t dev_idx, uint8_t stream_idx); |
| // Stop transferring and asynchronously deactivate the Audio Streaming |
| // interface (alt 0). true means that the deactivation request was submitted. |
| // Completion is reported through tuh_audio_event_cb(); no event is emitted |
| // when this function returns false. |
| bool tuh_audio_stop(uint8_t dev_idx, uint8_t stream_idx); |
| |
| // Frame-based transfer. One frame = channels * bytes per sample. |
| // tuh_audio_write() is valid only for TUH_AUDIO_STREAM_PLAYBACK streams, |
| // tuh_audio_read() only for TUH_AUDIO_STREAM_CAPTURE streams. |
| // Both functions are non-blocking and return immediately. |
| // Returns the number of frames actually written/read (0 on any error, |
| // including wrong direction, unconfigured/stopped stream, or full/empty FIFO). |
| uint32_t tuh_audio_write(uint8_t dev_idx, uint8_t stream_idx, const void *buffer, uint32_t frame_count); |
| uint32_t tuh_audio_read(uint8_t dev_idx, uint8_t stream_idx, void *buffer, uint32_t frame_count); |
| |
| // Number of frames that can be queued immediately for playback. |
| uint32_t tuh_audio_write_available(uint8_t dev_idx, uint8_t stream_idx); |
| // Number of captured frames that can be read immediately. |
| uint32_t tuh_audio_read_available(uint8_t dev_idx, uint8_t stream_idx); |
| |
| //--------------------------------------------------------------------+ |
| // Helpers |
| //--------------------------------------------------------------------+ |
| |
| // Container size in bytes of one sample for a given format. |
| static inline uint8_t tuh_audio_format_bytes(tuh_audio_format_t format) { |
| switch (format) { |
| case TUH_AUDIO_FORMAT_S8: |
| return 1; |
| case TUH_AUDIO_FORMAT_S16_LE: |
| return 2; |
| case TUH_AUDIO_FORMAT_S24_3LE: |
| return 3; |
| case TUH_AUDIO_FORMAT_S24_LE: |
| case TUH_AUDIO_FORMAT_S32_LE: |
| return 4; |
| default: |
| return 0; |
| } |
| } |
| |
| // Size in bytes of one frame (all channels) for a configuration. |
| static inline uint32_t tuh_audio_config_frame_size(const tuh_audio_stream_config_t *config) { |
| TU_ASSERT(config != NULL); |
| return (uint32_t)tuh_audio_format_bytes(config->format) * config->channels; |
| } |
| |
| //--------------------------------------------------------------------+ |
| // Device Info |
| //--------------------------------------------------------------------+ |
| |
| // Check if Audio device is mounted |
| bool tuh_audio_mounted(uint8_t idx); |
| // Get device address of Audio device |
| uint8_t tuh_audio_get_dev_addr(uint8_t idx); |
| // True when the stream's Feature Unit supports master mute control. |
| bool tuh_audio_mute_supported(uint8_t idx, uint8_t stream_idx); |
| // Get the cached volume range. The driver reads the master channel when it |
| // supports volume, otherwise the first logical channel with volume control. |
| // This typed API assumes logical channels use the same range; applications |
| // needing per-channel ranges can use tuh_audio_control_xfer(). |
| bool tuh_audio_volume_range_get(uint8_t idx, uint8_t stream_idx, tuh_audio_volume_range_t *range); |
| |
| //--------------------------------------------------------------------+ |
| // Control Request API |
| //--------------------------------------------------------------------+ |
| |
| // Submit a class-specific request to an entity on the Audio Control interface. |
| // request is the protocol-specific UAC request code. buffer contains the raw |
| // little-endian control payload. For an asynchronous transfer, buffer must |
| // remain valid until complete_cb is invoked. |
| bool tuh_audio_control_xfer(uint8_t idx, uint8_t entity_id, tusb_dir_t direction, uint8_t request, |
| uint8_t control_selector, uint8_t channel, void *buffer, uint16_t length, |
| tuh_xfer_cb_t complete_cb, uintptr_t user_data); |
| |
| // Master mute and volume controls. Capability and range information is cached |
| // before tuh_audio_mount_cb() is invoked. Volume channel 0 selects the master; |
| // a SET falls back to writing every logical channel when the master is not |
| // writable and all logical channels advertise write access. The completion |
| // callback is invoked once after the entire operation. A nonzero volume |
| // channel directly selects that 1-based Feature Unit logical channel. |
| // Per-channel capability is not cached; an unsupported channel is reported by |
| // the control transfer. |
| // |
| // Volume SET accepts TUH_AUDIO_VOLUME_SILENCE or a value within the cached |
| // range; finite values are rounded to the nearest resolution step measured |
| // from the range minimum. |
| bool tuh_audio_mute_set(uint8_t idx, uint8_t stream_idx, bool mute, tuh_xfer_cb_t complete_cb, uintptr_t user_data); |
| bool tuh_audio_mute_get(uint8_t idx, uint8_t stream_idx, bool *mute, tuh_xfer_cb_t complete_cb, uintptr_t user_data); |
| bool tuh_audio_volume_set(uint8_t idx, uint8_t stream_idx, uint8_t channel, int16_t volume, tuh_xfer_cb_t complete_cb, |
| uintptr_t user_data); |
| bool tuh_audio_volume_get(uint8_t idx, uint8_t stream_idx, uint8_t channel, int16_t *volume, tuh_xfer_cb_t complete_cb, |
| uintptr_t user_data); |
| |
| //--------------------------------------------------------------------+ |
| // Synchronous control requests block until the transfer completes and return |
| // its result. actual_len may be NULL when the received length is not needed. |
| // Only use when audio streaming is stopped, otherwise the stream's isochronous |
| // transfers may be disrupted and creating audible artifacts ! |
| //--------------------------------------------------------------------+ |
| tusb_xfer_result_t tuh_audio_control_xfer_sync(uint8_t idx, uint8_t entity_id, tusb_dir_t direction, uint8_t request, |
| uint8_t control_selector, uint8_t channel, void *buffer, uint16_t length, |
| uint32_t *actual_len); |
| |
| TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_mute_set_sync(uint8_t idx, uint8_t stream_idx, |
| bool mute) { |
| TU_API_SYNC(tuh_audio_mute_set, idx, stream_idx, mute); |
| } |
| |
| TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_mute_get_sync(uint8_t idx, uint8_t stream_idx, |
| bool *mute) { |
| TU_API_SYNC(tuh_audio_mute_get, idx, stream_idx, mute); |
| } |
| |
| TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_volume_set_sync(uint8_t idx, uint8_t stream_idx, |
| uint8_t channel, int16_t volume) { |
| TU_API_SYNC(tuh_audio_volume_set, idx, stream_idx, channel, volume); |
| } |
| |
| TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_volume_get_sync(uint8_t idx, uint8_t stream_idx, |
| uint8_t channel, int16_t *volume) { |
| TU_API_SYNC(tuh_audio_volume_get, idx, stream_idx, channel, volume); |
| } |
| |
| //--------------------------------------------------------------------+ |
| // Callbacks (Weak is optional) |
| //--------------------------------------------------------------------+ |
| |
| // Invoked after the Audio Control and Streaming descriptors have been |
| // validated during enumeration, before tuh_audio_mount_cb(). The interface is |
| // not mounted yet and control requests must not be submitted from this |
| // callback. Applications may inspect or copy descriptors needed for later raw |
| // entity control requests. |
| void tuh_audio_descriptor_cb(uint8_t idx, const tuh_audio_descriptor_cb_t *desc_cb_data); |
| |
| // Invoked when device with Audio interface is mounted |
| void tuh_audio_mount_cb(uint8_t idx); |
| |
| // Invoked when device with Audio interface is un-mounted |
| void tuh_audio_umount_cb(uint8_t idx); |
| |
| // Invoked when an isochronous IN transfer completes successfully: the |
| // received data is already queued into the stream's capture FIFO. |
| void tuh_audio_capture_cb(uint8_t idx, uint8_t stream_idx, uint16_t xferred_bytes); |
| |
| // Invoked after a successful isochronous OUT transfer, before the next packet |
| // is prepared. After this callback returns, the driver submits queued audio |
| // from the stream FIFO, or silence when a complete packet is unavailable. |
| void tuh_audio_playback_cb(uint8_t idx, uint8_t stream_idx, uint16_t xferred_bytes); |
| |
| // Reports completion of asynchronous start/stop operations and unrecoverable |
| // transfer failures. START_COMPLETE is emitted after the complete activation |
| // sequence and initial endpoint transfers are submitted. XFER_FAILED means the |
| // HCD could not submit a transfer or completed it unsuccessfully; it is not a |
| // notification for an individual dropped isochronous packet. The driver stops |
| // the stream before reporting START_COMPLETE failure or XFER_FAILED. |
| void tuh_audio_event_cb(uint8_t idx, uint8_t stream_idx, tuh_audio_event_t event, tusb_xfer_result_t result); |
| |
| //--------------------------------------------------------------------+ |
| // Internal Class Driver API |
| //--------------------------------------------------------------------+ |
| bool audioh_init(void); |
| bool audioh_deinit(void); |
| uint16_t audioh_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len); |
| bool audioh_set_config(uint8_t dev_addr, uint8_t itf_num); |
| bool audioh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); |
| void audioh_close(uint8_t daddr); |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif /* TUSB_AUDIO_HOST_H_ */ |