mirror of
https://github.com/hathach/tinyusb.git
synced 2025-02-22 15:40:06 +00:00
UAC2: Implement feedback by fifo counting.
This commit is contained in:
parent
ee9ad0f184
commit
1b66c148cc
@ -2,6 +2,7 @@
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Reinhard Panhuber, Jerzy Kasenberg
|
||||
* Copyright (c) 2023 HiFiPhile
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -319,7 +320,8 @@ typedef struct
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
struct {
|
||||
CFG_TUSB_MEM_ALIGN uint32_t value; // Feedback value for asynchronous mode (in 16.16 format).
|
||||
CFG_TUSB_MEM_ALIGN uint32_t send_buf;
|
||||
uint32_t value; // Feedback value for asynchronous mode (in 16.16 format).
|
||||
uint32_t min_value; // min value according to UAC2 FMT-2.0 section 2.3.1.1.
|
||||
uint32_t max_value; // max value according to UAC2 FMT-2.0 section 2.3.1.1.
|
||||
|
||||
@ -335,12 +337,12 @@ typedef struct
|
||||
uint32_t mclk_freq;
|
||||
}fixed;
|
||||
|
||||
#if 0 // implement later
|
||||
struct {
|
||||
uint32_t nominal_value;
|
||||
uint32_t threshold_bytes;
|
||||
uint32_t nom_value; // In 16.16 format
|
||||
uint32_t fifo_lvl_avg; // In 16.16 format
|
||||
uint16_t fifo_lvl_thr; // fifo level threshold
|
||||
uint16_t rate_const[2]; // pre-computed feedback/fifo_depth rate
|
||||
}fifo_count;
|
||||
#endif
|
||||
}compute;
|
||||
|
||||
} feedback;
|
||||
@ -473,7 +475,8 @@ static uint16_t audiod_tx_packet_size(const uint16_t* norminal_size, uint16_t da
|
||||
#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);
|
||||
static bool audiod_set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq);
|
||||
static void audiod_fb_fifo_count_update(audiod_function_t* audio, uint16_t lvl_new);
|
||||
#endif
|
||||
|
||||
bool tud_audio_n_mounted(uint8_t func_id)
|
||||
@ -566,7 +569,7 @@ static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t
|
||||
TU_VERIFY(tud_audio_rx_done_pre_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
|
||||
}
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
|
||||
#if CFG_TUD_AUDIO_ENABLE_DECODING
|
||||
|
||||
switch (audio->format_type_rx)
|
||||
{
|
||||
@ -615,6 +618,13 @@ static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t
|
||||
TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz), false);
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
if(audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FIFO_COUNT)
|
||||
{
|
||||
audiod_fb_fifo_count_update(audio, tu_fifo_count(&audio->ep_out_ff));
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// Call a weak callback here - a possibility for user to get informed decoding was completed
|
||||
@ -725,6 +735,13 @@ static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, u
|
||||
// Number of bytes should be a multiple of CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX but checking makes no sense - no way to correct it
|
||||
// TU_VERIFY(cnt != n_bytes);
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
if(audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FIFO_COUNT)
|
||||
{
|
||||
audiod_fb_fifo_count_update(audio, tu_fifo_count(&audio->rx_supp_ff[0]));
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif //CFG_TUD_AUDIO_ENABLE_DECODING
|
||||
@ -1067,9 +1084,29 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
|
||||
// This function is called once a transmit of a feedback packet was successfully completed. Here, we get the next feedback value to be sent
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
static inline bool audiod_fb_send(uint8_t rhport, audiod_function_t *audio)
|
||||
static inline bool audiod_fb_send(audiod_function_t *audio)
|
||||
{
|
||||
return usbd_edpt_xfer(rhport, audio->ep_fb, (uint8_t *) &audio->feedback.value, 4);
|
||||
// Format the feedback value
|
||||
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION
|
||||
if ( TUSB_SPEED_FULL == tud_speed_get() )
|
||||
{
|
||||
uint8_t * fb = (uint8_t *) &audio->feedback.send_buf;
|
||||
|
||||
// For FS format is 10.14
|
||||
*(fb++) = (audio->feedback.value >> 2) & 0xFF;
|
||||
*(fb++) = (audio->feedback.value >> 10) & 0xFF;
|
||||
*(fb++) = (audio->feedback.value >> 18) & 0xFF;
|
||||
// 4th byte is needed to work correctly with MS Windows
|
||||
*fb = 0;
|
||||
} else
|
||||
{
|
||||
value = audio->feedback.value;
|
||||
}
|
||||
#else
|
||||
audio->feedback.send_buf = audio->feedback.value;
|
||||
#endif
|
||||
|
||||
return usbd_edpt_xfer(audio->rhport, audio->ep_fb, (uint8_t *) &audio->feedback.send_buf, 4);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1861,7 +1898,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
|
||||
|
||||
// Minimal/Maximum value in 16.16 format for full speed (1ms per frame) or high speed (125 us per frame)
|
||||
uint32_t const frame_div = (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000;
|
||||
audio->feedback.min_value = (fb_param.sample_freq/frame_div - 1) << 16;
|
||||
audio->feedback.min_value = ((fb_param.sample_freq - 1)/frame_div) << 16;
|
||||
audio->feedback.max_value = (fb_param.sample_freq/frame_div + 1) << 16;
|
||||
|
||||
switch(fb_param.method)
|
||||
@ -1869,20 +1906,27 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
|
||||
case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED:
|
||||
case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
|
||||
case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
|
||||
set_fb_params_freq(audio, fb_param.sample_freq, fb_param.frequency.mclk_freq);
|
||||
audiod_set_fb_params_freq(audio, fb_param.sample_freq, fb_param.frequency.mclk_freq);
|
||||
break;
|
||||
|
||||
#if 0 // implement later
|
||||
case AUDIO_FEEDBACK_METHOD_FIFO_COUNT:
|
||||
{
|
||||
uint64_t fb64 = ((uint64_t) fb_param.sample_freq) << 16;
|
||||
audio->feedback.compute.fifo_count.nominal_value = (uint32_t) (fb64 / frame_div);
|
||||
audio->feedback.compute.fifo_count.threshold_bytes = fb_param.fifo_count.threshold_bytes;
|
||||
|
||||
tud_audio_fb_set(audio->feedback.compute.fifo_count.nominal_value);
|
||||
/* Initialize the threshold level to half filled */
|
||||
uint16_t fifo_lvl_thr;
|
||||
#if CFG_TUD_AUDIO_ENABLE_DECODING
|
||||
fifo_lvl_thr = tu_fifo_depth(&audio->rx_supp_ff[0]) / 2;
|
||||
#else
|
||||
fifo_lvl_thr = tu_fifo_depth(&audio->ep_out_ff) / 2;
|
||||
#endif
|
||||
audio->feedback.compute.fifo_count.fifo_lvl_thr = fifo_lvl_thr;
|
||||
audio->feedback.compute.fifo_count.fifo_lvl_avg = ((uint32_t)fifo_lvl_thr) << 16;
|
||||
/* Avoid 64bit division */
|
||||
uint32_t nominal = ((fb_param.sample_freq / 100) << 16) / (frame_div / 100);
|
||||
audio->feedback.compute.fifo_count.nom_value = nominal;
|
||||
audio->feedback.compute.fifo_count.rate_const[0] = (audio->feedback.max_value - nominal) / fifo_lvl_thr;
|
||||
audio->feedback.compute.fifo_count.rate_const[1] = (nominal - audio->feedback.min_value) / fifo_lvl_thr;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
// nothing to do
|
||||
default: break;
|
||||
@ -2198,7 +2242,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
|
||||
if (!usbd_edpt_busy(rhport, audio->ep_fb))
|
||||
{
|
||||
// Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent
|
||||
return audiod_fb_send(rhport, audio);
|
||||
return audiod_fb_send(audio);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -2210,7 +2254,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
|
||||
|
||||
#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)
|
||||
static bool audiod_set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq)
|
||||
{
|
||||
// Check if frame interval is within sane limits
|
||||
// The interval value n_frames was taken from the descriptors within audiod_set_interface()
|
||||
@ -2244,6 +2288,38 @@ static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, u
|
||||
return true;
|
||||
}
|
||||
|
||||
static void audiod_fb_fifo_count_update(audiod_function_t* audio, uint16_t lvl_new)
|
||||
{
|
||||
/* Low-pass (averaging) filter */
|
||||
uint32_t lvl = audio->feedback.compute.fifo_count.fifo_lvl_avg;
|
||||
lvl = (uint32_t)(((uint64_t)lvl * 63 + ((uint32_t)lvl_new << 16)) >> 6);
|
||||
audio->feedback.compute.fifo_count.fifo_lvl_avg = lvl;
|
||||
|
||||
uint32_t const ff_lvl = lvl >> 16;
|
||||
uint16_t const ff_thr = audio->feedback.compute.fifo_count.fifo_lvl_thr;
|
||||
uint16_t const *rate = audio->feedback.compute.fifo_count.rate_const;
|
||||
|
||||
uint32_t feedback;
|
||||
|
||||
if(ff_lvl < ff_thr)
|
||||
{
|
||||
feedback = audio->feedback.compute.fifo_count.nom_value + (ff_thr - ff_lvl) * rate[0];
|
||||
} else
|
||||
{
|
||||
feedback = audio->feedback.compute.fifo_count.nom_value - (ff_lvl - ff_thr) * rate[1];
|
||||
}
|
||||
|
||||
if ( feedback > audio->feedback.max_value ) feedback = audio->feedback.max_value;
|
||||
if ( feedback < audio->feedback.min_value ) feedback = audio->feedback.min_value;
|
||||
audio->feedback.value = feedback;
|
||||
|
||||
// Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value
|
||||
if (!usbd_edpt_busy(audio->rhport, audio->ep_fb))
|
||||
{
|
||||
audiod_fb_send(audio);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles)
|
||||
{
|
||||
audiod_function_t* audio = &_audiod_fct[func_id];
|
||||
@ -2280,6 +2356,21 @@ uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles)
|
||||
|
||||
return feedback;
|
||||
}
|
||||
|
||||
bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback)
|
||||
{
|
||||
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
|
||||
|
||||
_audiod_fct[func_id].feedback.value = feedback;
|
||||
|
||||
// Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value
|
||||
if (!usbd_edpt_busy(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_fb))
|
||||
{
|
||||
return audiod_fb_send(&_audiod_fct[func_id]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
TU_ATTR_FAST_FUNC void audiod_sof_isr (uint8_t rhport, uint32_t frame_count)
|
||||
@ -2691,44 +2782,8 @@ static uint16_t audiod_tx_packet_size(const uint16_t* norminal_size, uint16_t da
|
||||
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
|
||||
bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback)
|
||||
{
|
||||
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
|
||||
|
||||
// Format the feedback value
|
||||
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION
|
||||
if ( TUSB_SPEED_FULL == tud_speed_get() )
|
||||
{
|
||||
uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].feedback.value;
|
||||
|
||||
// For FS format is 10.14
|
||||
*(fb++) = (feedback >> 2) & 0xFF;
|
||||
*(fb++) = (feedback >> 10) & 0xFF;
|
||||
*(fb++) = (feedback >> 18) & 0xFF;
|
||||
// 4th byte is needed to work correctly with MS Windows
|
||||
*fb = 0;
|
||||
}else
|
||||
#else
|
||||
{
|
||||
// Send value as-is, caller will choose the appropriate format
|
||||
_audiod_fct[func_id].feedback.value = feedback;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value
|
||||
if (!usbd_edpt_busy(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_fb))
|
||||
{
|
||||
return audiod_fb_send(_audiod_fct[func_id].rhport, &_audiod_fct[func_id]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// No security checks here - internal function only which should always succeed
|
||||
uint8_t audiod_get_audio_fct_idx(audiod_function_t * audio)
|
||||
static uint8_t audiod_get_audio_fct_idx(audiod_function_t * audio)
|
||||
{
|
||||
for (uint8_t cnt=0; cnt < CFG_TUD_AUDIO; cnt++)
|
||||
{
|
||||
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2020 Ha Thach (tinyusb.org)
|
||||
* Copyright (c) 2020 Reinhard Panhuber
|
||||
* Copyright (c) 2023 HiFiPhile
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -487,7 +488,6 @@ TU_ATTR_WEAK void tud_audio_fb_done_cb(uint8_t func_id);
|
||||
// feedback = n_cycles / n_frames * f_s / f_m in 16.16 format, where n_cycles are the number of main clock cycles within fb_n_frames
|
||||
|
||||
bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback);
|
||||
static inline bool tud_audio_fb_set(uint32_t feedback);
|
||||
|
||||
// Update feedback value with passed cycles since last time this update function is called.
|
||||
// Typically called within tud_audio_sof_isr(). Required tud_audio_feedback_params_cb() is implemented
|
||||
@ -500,9 +500,7 @@ enum {
|
||||
AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED,
|
||||
AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT,
|
||||
AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2,
|
||||
|
||||
// impelemnt later
|
||||
// AUDIO_FEEDBACK_METHOD_FIFO_COUNT
|
||||
AUDIO_FEEDBACK_METHOD_FIFO_COUNT
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@ -514,11 +512,6 @@ typedef struct {
|
||||
uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to which sample clock is based on
|
||||
}frequency;
|
||||
|
||||
#if 0 // implement later
|
||||
struct {
|
||||
uint32_t threshold_bytes; // minimum number of bytes received to be considered as filled/ready
|
||||
}fifo_count;
|
||||
#endif
|
||||
};
|
||||
}audio_feedback_params_t;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user