Add flow control for IN transfer.

This commit is contained in:
HiFiPhile 2023-10-17 09:46:01 +02:00
parent 118823c254
commit 656e1e416a
5 changed files with 187 additions and 21 deletions

View File

@ -69,8 +69,13 @@ uint8_t clkValid;
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
#if CFG_TUD_AUDIO_ENABLE_ENCODING
// Audio test data, each buffer contains 2 channels, buffer[0] for CH0-1, buffer[1] for CH1-2
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ/2]; // Ensure half word aligned
#else
// Audio test data, 4 channels muxed together, buffer[0] for CH0, buffer[1] for CH1, buffer[2] for CH2, buffer[3] for CH3
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_EP_SZ_IN]; // Ensure half word aligned
#endif
void led_blinking_task(void);
void audio_task(void);
@ -97,6 +102,7 @@ int main(void)
sampleFreqRng.subrange[0].bRes = 0;
// Generate dummy data
#if CFG_TUD_AUDIO_ENABLE_ENCODING
uint16_t * p_buff = i2s_dummy_buffer[0];
uint16_t dataVal = 1;
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
@ -116,6 +122,23 @@ int main(void)
float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000);
*p_buff++ = (uint16_t)(sinf(t) * 25) + 200;
}
#else
uint16_t * p_buff = i2s_dummy_buffer;
uint16_t dataVal = 1;
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
{
// CH0 saw wave
*p_buff++ = dataVal;
// CH1 inverted saw wave
*p_buff++ = 60 + AUDIO_SAMPLE_RATE/1000 - dataVal;
dataVal++;
// CH3 square wave
*p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 120:170;
// CH4 sinus wave
float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000);
*p_buff++ = (uint16_t)(sinf(t) * 25) + 200;
}
#endif
while (1)
{
@ -384,6 +407,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
{
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
// Set sample rate for flow control
tud_audio_set_tx_flow_control(sampFreq);
return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
case AUDIO_CS_REQ_RANGE:
@ -429,12 +454,15 @@ bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, u
// tud_audio_write_support_ff(channel, data, samples * N_BYTES_PER_SAMPLE * N_CHANNEL_PER_FIFO);
// }
#if CFG_TUD_AUDIO_ENABLE_ENCODING
// Write I2S buffer into FIFO
for (uint8_t cnt=0; cnt < 2; cnt++)
{
tud_audio_write_support_ff(cnt, i2s_dummy_buffer[cnt], AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX);
}
#else
tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX);
#endif
return true;
}

View File

@ -10,11 +10,11 @@ if __name__ == '__main__':
# print(sd.query_devices())
fs = 48000 # Sample rate
duration = 20e-3 # Duration of recording
duration = 1 # Duration of recording
if platform.system() == 'Windows':
# WDM-KS is needed since there are more than one MicNode device APIs (at least in Windows)
device = 'Microphone (MicNode_4_Ch), Windows WDM-KS'
device = 'Microphone (MicNode_4_Ch), Windows WASAPI'
elif platform.system() == 'Darwin':
device = 'MicNode_4_Ch'
else:
@ -28,8 +28,7 @@ if __name__ == '__main__':
time = np.arange(0, duration, 1 / fs) # time vector
# strip starting zero
myrecording = myrecording[100:]
time = time[100:]
plt.plot(time, myrecording)
plt.xlabel('Time [s]')
plt.ylabel('Amplitude')

View File

@ -114,14 +114,25 @@ extern "C" {
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 4 // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup
#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_ENABLE_ENCODING 0
#if CFG_TUD_AUDIO_ENABLE_ENCODING
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_EP_SZ_IN
#define CFG_TUD_AUDIO_ENABLE_ENCODING 1
#define CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING 1
#define CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX 2 // One I2S stream contains two channels, each stream is saved within one support FIFO - this value is currently fixed, the driver does not support a changing value
#define CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX)
#define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ (CFG_TUD_AUDIO_EP_SZ_IN / CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO)
#define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ 4 * (CFG_TUD_AUDIO_EP_SZ_IN / CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO)
#else
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX 4 * CFG_TUD_AUDIO_EP_SZ_IN
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ 4 * CFG_TUD_AUDIO_EP_SZ_IN
#endif
#ifdef __cplusplus
}

View File

@ -364,14 +364,21 @@ typedef struct
#endif
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
uint32_t sample_rate_tx;
uint16_t packet_sz_tx[3];
uint8_t bclock_id_tx;
uint8_t interval_tx;
#endif
// Encoding parameters - parameters are set when alternate AS interface is set by host
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
#if CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL)
audio_format_type_t format_type_tx;
uint8_t n_channels_tx;
uint8_t n_bytes_per_sampe_tx;
#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
audio_data_format_type_I_t format_type_I_tx;
uint8_t n_bytes_per_sampe_tx;
uint8_t n_channels_per_ff_tx;
uint8_t n_ff_used_tx;
#endif
@ -444,7 +451,7 @@ static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id);
static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id);
static uint8_t audiod_get_audio_fct_idx(audiod_function_t * audio);
#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * p_desc, uint8_t const * p_desc_end, uint8_t const as_itf);
static inline uint8_t tu_desc_subtype(void const* desc)
@ -453,6 +460,10 @@ static inline uint8_t tu_desc_subtype(void const* desc)
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
static bool audiod_tx_calc_packet_size(const uint16_t* norminal_size, uint16_t data_size, uint16_t fifo_size, uint16_t* packet_size);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq);
#endif
@ -821,6 +832,57 @@ uint16_t tud_audio_int_ctr_n_write(uint8_t func_id, uint8_t const* buffer, uint1
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
bool tud_audio_n_set_tx_flow_control(uint8_t func_id, uint32_t sample_rate)
{
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
audiod_function_t* audio = &_audiod_fct[func_id];
TU_VERIFY(audio->format_type_tx == AUDIO_FORMAT_TYPE_I);
TU_VERIFY(audio->n_channels_tx);
TU_VERIFY(audio->n_bytes_per_sampe_tx);
TU_VERIFY(audio->interval_tx);
if (sample_rate == 0)
{
audio->packet_sz_tx[0] = 0;
audio->packet_sz_tx[1] = 0;
audio->packet_sz_tx[2] = 0;
return false;
}
const uint8_t interval = (tud_speed_get() == TUSB_SPEED_FULL) ? audio->interval_tx : 1 << (audio->interval_tx - 1);
const uint32_t sample_normimal = sample_rate * interval / ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000);
const uint32_t sample_reminder = sample_rate * interval % ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000);
const uint16_t packet_sz_tx_min = (sample_normimal - 1) * audio->n_channels_tx * audio->n_bytes_per_sampe_tx;
const uint16_t packet_sz_tx_norm = sample_normimal * audio->n_channels_tx * audio->n_bytes_per_sampe_tx;
const uint16_t packet_sz_tx_max = (sample_normimal + 1) * audio->n_channels_tx * audio->n_bytes_per_sampe_tx;
TU_ASSERT(packet_sz_tx_max <= audio->ep_in_sz);
// Frmt20.pdf 2.3.1.1 USB Packets
if (sample_reminder)
{
// All virtual frame packets must either contain INT(nav) audio slots (small VFP) or INT(nav)+1 (large VFP) audio slots
audio->packet_sz_tx[0] = packet_sz_tx_norm;
audio->packet_sz_tx[1] = packet_sz_tx_norm;
audio->packet_sz_tx[2] = packet_sz_tx_max;
} else
{
// In the case where nav = INT(nav), ni may vary between INT(nav)-1 (small VFP), INT(nav)
// (medium VFP) and INT(nav)+1 (large VFP).
audio->packet_sz_tx[0] = packet_sz_tx_min;
audio->packet_sz_tx[1] = packet_sz_tx_norm;
audio->packet_sz_tx[2] = packet_sz_tx_max;
}
return true;
}
#endif
// This function is called once a transmit of an audio packet was successfully completed. Here, we encode samples and place it in IN EP's buffer for next transmission.
// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_ENABLE_ENCODING = 0 and use tud_audio_n_write.
@ -886,9 +948,16 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t * audio)
#else
// No support FIFOs, if no linear buffer required schedule transmit, else put data into linear buffer and schedule
#if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
uint16_t tgt_packet_sz;
// packet_sz_tx is based on total packet size, here we want size for each support buffer.
if (audiod_tx_calc_packet_size(audio->packet_sz_tx, tu_fifo_count(&audio->ep_in_ff), audio->ep_in_ff.depth, &tgt_packet_sz))
n_bytes_tx = tgt_packet_sz;
else
n_bytes_tx = tu_min16(tu_fifo_count(&audio->ep_in_ff), audio->ep_in_sz);
#else
n_bytes_tx = tu_min16(tu_fifo_count(&audio->ep_in_ff), audio->ep_in_sz); // Limit up to max packet size, more can not be done for ISO
#endif
#if USE_LINEAR_BUFFER_TX
tu_fifo_read_n(&audio->ep_in_ff, audio->lin_buf_in, n_bytes_tx);
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
@ -987,7 +1056,6 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
// Determine amount of samples
uint8_t const n_ff_used = audio->n_ff_used_tx;
uint16_t const nBytesToCopy = audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx;
uint16_t const capPerFF = audio->ep_in_sz / n_ff_used; // Sample capacity per FIFO in bytes
uint16_t nBytesPerFFToSend = tu_fifo_count(&audio->tx_supp_ff[0]);
uint8_t cnt_ff;
@ -1001,14 +1069,24 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
}
}
// Check if there is enough
#if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
uint16_t tgt_packet_sz;
// packet_sz_tx is based on total packet size, here we want size for each support buffer.
if (audiod_tx_calc_packet_size(audio->packet_sz_tx, nBytesPerFFToSend * n_ff_used, audio->tx_supp_ff[0].depth * n_ff_used, &tgt_packet_sz))
nBytesPerFFToSend = tgt_packet_sz / n_ff_used;
#endif
// Check if there is enough data
if (nBytesPerFFToSend == 0) return 0;
// Limit to maximum sample number - THIS IS A POSSIBLE ERROR SOURCE IF TOO MANY SAMPLE WOULD NEED TO BE SENT BUT CAN NOT!
nBytesPerFFToSend = tu_min16(nBytesPerFFToSend, capPerFF);
#if !CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
// Round to full number of samples (flooring)
nBytesPerFFToSend = (nBytesPerFFToSend / nBytesToCopy) * nBytesToCopy;
uint16_t const nSlotSize = audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx;
nBytesPerFFToSend = (nBytesPerFFToSend / nSlotSize) * nSlotSize;
#endif
// Encode
uint8_t * dst;
@ -1489,6 +1567,9 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
#if CFG_TUD_AUDIO_ENABLE_EP_IN
ep_in = desc_ep->bEndpointAddress;
ep_in_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_in_size);
#if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
_audiod_fct[i].interval_tx = desc_ep->bInterval;
#endif
#endif
} else
{
@ -1607,6 +1688,11 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
audio->ep_in = 0; // Necessary?
#if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
audio->packet_sz_tx[0] = 0;
audio->packet_sz_tx[1] = 0;
audio->packet_sz_tx[2] = 0;
#endif
}
#endif // CFG_TUD_AUDIO_ENABLE_EP_IN
@ -1657,7 +1743,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
// Find correct interface
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == alt)
{
#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
uint8_t const * p_desc_parse_for_params = p_desc;
#endif
// From this point forward follow the EP descriptors associated to the current alternate setting interface - Open EPs if necessary
@ -1686,12 +1772,13 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
audio->ep_in_sz = tu_edpt_packet_size(desc_ep);
// If software encoding is enabled, parse for the corresponding parameters - doing this here means only AS interfaces with EPs get scanned for parameters
#if CFG_TUD_AUDIO_ENABLE_ENCODING
#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
audiod_parse_for_AS_params(audio, p_desc_parse_for_params, p_desc_end, itf);
// Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
const uint16_t active_fifo_depth = (uint16_t) ((audio->tx_supp_ff_sz_max / audio->n_bytes_per_sampe_tx) * audio->n_bytes_per_sampe_tx);
#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
const uint16_t active_fifo_depth = (uint16_t) ((audio->tx_supp_ff_sz_max / (audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx))
* (audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx));
for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++)
{
tu_fifo_config(&audio->tx_supp_ff[cnt], audio->tx_supp_ff[cnt].buffer, active_fifo_depth, 1, true);
@ -2404,7 +2491,7 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id)
return false;
}
#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
// p_desc points to the AS interface of alternate setting zero
// itf is the interface number of the corresponding interface - we check if the interface belongs to EP in or EP out to see if it is a TX or RX parameter
// Currently, only AS interfaces with an EP (in or out) are supposed to be parsed for!
@ -2455,7 +2542,7 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
}
// Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING || CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_FORMAT_TYPE && ((audio_desc_type_I_format_t const * )p_desc)->bFormatType == AUDIO_FORMAT_TYPE_I)
{
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
@ -2491,6 +2578,31 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
static bool audiod_tx_calc_packet_size(const uint16_t* norminal_size, uint16_t data_size, uint16_t fifo_size, uint16_t* packet_size)
{
TU_VERIFY(norminal_size[1]);
// This flow control method need a FIFO size of 4*Navg
TU_VERIFY(norminal_size[1] <= fifo_size * 4);
if (data_size < norminal_size[0])
*packet_size = 0;
else
{
uint16_t slot_size = norminal_size[2] - norminal_size[1];
if (data_size < fifo_size / 2 - slot_size)
*packet_size = norminal_size[0];
else if (data_size > fifo_size / 2 + slot_size)
*packet_size = norminal_size[2];
else
*packet_size = norminal_size[1];
}
return true;
}
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback)

View File

@ -181,6 +181,11 @@
#endif
#endif
// (For TYPE-I format only) Flow control is necessary to allow IN ep send correct amount of data, unless it's a virtual device where data is perfectly synchronized to USB clock.
#ifndef CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
#define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1
#endif
// Enable/disable feedback EP (required for asynchronous RX applications)
#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback - 0 or 1
@ -392,6 +397,10 @@ tu_fifo_t* tud_audio_n_get_tx_support_ff (uint8_t func_id, uint8_t ff_i
uint16_t tud_audio_int_ctr_n_write (uint8_t func_id, uint8_t const* buffer, uint16_t len);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
bool tud_audio_n_set_tx_flow_control (uint8_t func_id, uint32_t sample_rate);
#endif
//--------------------------------------------------------------------+
// Application API (Interface0)
//--------------------------------------------------------------------+
@ -670,6 +679,13 @@ static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t l
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
static inline bool tud_audio_set_tx_flow_control(uint32_t sample_rate)
{
return tud_audio_n_set_tx_flow_control(0, sample_rate);
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
static inline bool tud_audio_fb_set(uint32_t feedback)