From c5ba1ea8c17a379beb47d9689e85e2ce6e67206f Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 13 May 2022 22:54:47 +0700 Subject: [PATCH 1/7] changes proposal to audio feedback computation --- src/class/audio/audio_device.c | 239 ++++++++++++++------------------- src/class/audio/audio_device.h | 100 ++++---------- src/common/tusb_common.h | 6 - src/device/usbd.c | 6 +- src/device/usbd.h | 2 +- 5 files changed, 129 insertions(+), 224 deletions(-) diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index b0a49fb04f..c3a252a2cf 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -261,6 +261,13 @@ osal_mutex_def_t rx_supp_ff_mutex_rd_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO]; #endif #endif +enum { + FEEDBACK_COMPUTE_DISABLED, + FEEDBACK_COMPUTE_FLOAT, + FEEDBACK_COMPUTE_FIXED, + FEEDBACK_COMPUTE_POWER_OF_2, +}; + typedef struct { uint8_t rhport; @@ -305,38 +312,22 @@ typedef struct #endif #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_NO_SOF_BY_USER - uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format). - uint8_t fb_n_frames; // Number of (micro)frames used to estimate feedback value -#endif -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER - volatile uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format). - uint32_t fb_val_min; // Maximum allowed feedback value according to UAC2 FMT-2.0 section 2.3.1.1. - uint32_t fb_val_max; // Maximum allowed feedback value according to UAC2 FMT-2.0 section 2.3.1.1. - uint8_t fb_n_frames; // Number of (micro)frames used to estimate feedback value - volatile uint8_t fb_n_frames_current; // Current (micro)frame number - volatile uint32_t fb_n_cycles_old; // Old cycle count - uint32_t * fb_param_p_cycle_count; // Pointer to cycle counter + uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format). + uint8_t fb_n_frames; // Number of (micro)frames used to estimate feedback value + uint8_t fb_n_frames_shift; + uint8_t fb_compute_method; + + uint32_t fb_val_min; // Maximum allowed feedback value according to UAC2 FMT-2.0 section 2.3.1.1. + uint32_t fb_val_max; // Maximum allowed feedback value according to UAC2 FMT-2.0 section 2.3.1.1. -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT + // should be union uint8_t fb_power_of_two_val; -#endif -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT float fb_float_val; -#endif -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT - uint64_t fb_param_factor_N; // Numerator of feedback parameter coefficient - uint64_t fb_param_factor_D; // Denominator of feedback parameter coefficient -#endif -#endif // CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER - -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_USER - volatile uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format). - uint8_t fb_n_frames; // Number of (micro)frames used to estimate feedback value -#endif + uint32_t fb_param_factor_N; + uint32_t fb_param_factor_D; #endif // CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP #endif // CFG_TUD_AUDIO_ENABLE_EP_OUT @@ -453,8 +444,8 @@ static inline uint8_t tu_desc_subtype(void const* desc) } #endif -#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP && (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER) -static bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback); +#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP +bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback); #endif bool tud_audio_n_mounted(uint8_t func_id) @@ -1695,16 +1686,11 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 1) // Check if usage is explicit data feedback { audio->ep_fb = ep_addr; - audio->fb_n_frames = desc_ep->bInterval; - -#if ((CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_USER) || (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER)) - usbd_sof_enable(rhport, true); // Enable SOF interrupt -#endif + audio->fb_n_frames = 1 << (desc_ep->bInterval -1); + audio->fb_n_frames_shift = desc_ep->bInterval -1; -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER - audio->fb_n_frames_current = 0; - audio->fb_n_cycles_old = 0; -#endif + // Enable SOF interrupt if callback is implemented + if (tud_audio_sof_isr) usbd_sof_enable(rhport, true); // // Invoke callback after ep_out is set // if (audio->ep_out != 0) @@ -1725,6 +1711,24 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * // Invoke one callback for a final set interface if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request)); + // Prepare feedback computation if callback is available + if (tud_audio_feedback_params_cb) + { + uint32_t sample_freq = 0; + uint32_t mclk_freq = 0; + uint8_t fixed_point = 0; + tud_audio_feedback_params_cb(func_id, alt, &sample_freq, &mclk_freq, &fixed_point); + + if ( sample_freq == 0 || mclk_freq == 0 ) + { + audio->fb_compute_method = FEEDBACK_COMPUTE_DISABLED; + }else + { + audio->fb_compute_method = fixed_point ? FEEDBACK_COMPUTE_FIXED : FEEDBACK_COMPUTE_FLOAT; + set_fb_params(audio, sample_freq, mclk_freq); + } + } + // We are done - abort loop break; } @@ -1734,22 +1738,17 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * } // Disable SOF interrupt if no driver has any enabled feedback EP -#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP && ((CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_USER) || (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER)) - bool disable = true; - for(uint8_t i=0; i < CFG_TUD_AUDIO; i++) { if (_audiod_fct[i].ep_fb != 0) { disable = false; + break; } } - if (disable) usbd_sof_enable(rhport, false); -#endif - tud_control_status(rhport, p_request); return true; @@ -2022,7 +2021,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 // Transmission of feedback EP finished if (_audiod_fct[func_id].ep_fb == ep_addr) { - if (tud_audio_fb_done_cb) TU_VERIFY(tud_audio_fb_done_cb(rhport)); + if (tud_audio_fb_done_cb) tud_audio_fb_done_cb(func_id); // Schedule a transmit with the new value if EP is not busy if (!usbd_edpt_busy(rhport, _audiod_fct[func_id].ep_fb)) @@ -2038,81 +2037,93 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 return false; } -#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP && ((CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_NO_SOF_BY_USER) || (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_USER)) +#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP uint8_t tud_audio_n_get_fb_n_frames(uint8_t func_id) { return _audiod_fct[func_id].fb_n_frames; } #endif -#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP && (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER) +#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -// This function must be called from the user within the tud_audio_set_itf_cb(rhport, p_request) callback function, where p_request needs to be checked as -// uint8_t const itf = tu_u16_low(p_request->wIndex); -// uint8_t const alt = tu_u16_low(p_request->wValue); -// such that tud_audio_set_fb_params() gets called with the parameters corresponding to the defined interface and alternate setting -// Also, start the main clock cycle counter (or reset its value) within tud_audio_set_itf_cb() -TU_ATTR_WEAK bool tud_audio_set_fb_params(uint8_t func_id, uint32_t f_m, uint32_t f_s, uint32_t * p_cycle_count) +static bool set_fb_params(audiod_function_t* audio, uint32_t f_s, uint32_t f_m) { - audiod_function_t* audio = &_audiod_fct[func_id]; - audio->fb_param_p_cycle_count = p_cycle_count; - // Check if frame interval is within sane limits // The interval value audio->fb_n_frames was taken from the descriptors within audiod_set_interface() - // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s / f_m) for high speed - this lower limit ensures the measures feedback value has sufficient precision - if ((TUSB_SPEED_FULL == tud_speed_get() && ((2^10 * f_s / f_m) + 1) > audio->fb_n_frames) || (TUSB_SPEED_HIGH == tud_speed_get() && ((2^13 * f_s / f_m) + 1) > audio->fb_n_frames)) + // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s / f_m) for high speed + // this lower limit ensures the measures feedback value has sufficient precision + uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13; + if ( (((1UL << k) * f_s / f_m) + 1) > audio->fb_n_frames ) { - TU_LOG2(" UAC2 feedback interval too small\r\n"); TU_BREAKPOINT(); return false; + TU_LOG1(" UAC2 feedback interval too small\r\n"); TU_BREAKPOINT(); return false; } - -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT // Check if parameters really allow for a power of two division - if ((f_m % f_s) != 0 || !tu_is_power_of_two(audio->fb_n_frames * f_m / f_s)) + if ((f_m % f_s) == 0 && tu_is_power_of_two(f_m / f_s)) { - TU_LOG2(" FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT not possible!\r\n"); TU_BREAKPOINT(); return false; + audio->fb_compute_method = FEEDBACK_COMPUTE_POWER_OF_2; + audio->fb_power_of_two_val = 16 - audio->fb_n_frames_shift - tu_log2(f_m / f_s); + }else if ( audio->fb_compute_method == FEEDBACK_COMPUTE_FLOAT) + { + audio->fb_float_val = (float)f_s / f_m * (1UL << (16 - audio->fb_n_frames_shift)); + }else + { + audio->fb_param_factor_N = f_s; + audio->fb_param_factor_D = f_m; } - audio->fb_power_of_two_val = 16 - tu_log2(audio->fb_n_frames * f_m / f_s); -#endif + // 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->fb_val_min = (f_s/frame_div - 1) << 16; + audio->fb_val_max = (f_s/frame_div + 1) << 16; -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT - audio->fb_float_val = (float)f_s / audio->fb_n_frames / f_m * (1 << 16); + return true; +} #endif -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT - - // f_s max is 2^19-1 = 524287 Hz - // f_m max is 2^29/(1 ms * n_frames) for full speed and 2^29/(125 us * n_frames) for high speed, this means for f_m fitting in an uint32, n_frames must be < 125 for full speed and < 1000 for high speed (1000 does not fit into uint8 so the maximum possible value cannot even be reached by UAC2 - we don't need to check for it) +uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) +{ + audiod_function_t* audio = &_audiod_fct[func_id]; + uint32_t feedback; - if ((f_s > (2^19)-1) || (TUSB_SPEED_FULL == tud_speed_get() && (audio->fb_n_frames > 125))) + switch (audio->fb_compute_method) { - // If this check fails, not every thing is lost - you need to re-evaluate the scaling factors of the parameters such that the numbers fit into uint64 again. feedback = n_cycles * S_c / (n_frames * S_n) * f_s * S_s / (f_m * S_m). In the end S_c*S_s / (S_n * S_m) = 2^16 for a 16.16 fixed point precision. If you find something, define your own function of tud_audio_set_feedback_params_fm_fs() and audiod_sof() and use your values - TU_LOG2(" FEEDBACK_DETERMINATION_MODE_FIXED_POINT not possible!\r\n"); TU_BREAKPOINT(); return false; - } + case FEEDBACK_COMPUTE_POWER_OF_2: + feedback = cycles << audio->fb_power_of_two_val; + break; - audio->fb_param_factor_N = (uint64_t)f_s << 13; - audio->fb_param_factor_D = (uint64_t)f_m * audio->fb_n_frames; -#endif + case FEEDBACK_COMPUTE_FLOAT: + feedback = (uint32_t) ((float) cylces * audio->fb_float_val); + break; + + case FEEDBACK_COMPUTE_FIXED: + { + uint64_t fb64 = (((uint64_t) cycles) * audio->fb_param_factor_N) << (16 - audio->fb_n_frames_shift); + feedback = (uint32_t) (fb64 / audio->fb_param_factor_D); + } + break; - audio->fb_val_min = ((TUSB_SPEED_FULL == tud_speed_get() ? (f_s/1000) : (f_s/8000)) - 1) << 16; // Minimal value in 16.16 format for full speed (1ms per frame) or high speed (125 us per frame) - audio->fb_val_max = ((TUSB_SPEED_FULL == tud_speed_get() ? (f_s/1000) : (f_s/8000)) + 1) << 16; // Maximum value in 16.16 format + default: return 0; + } - return true; + // For Windows: https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers + // The size of isochronous packets created by the device must be within the limits specified in FMT-2.0 section 2.3.1.1. + // This means that the deviation of actual packet size from nominal size must not exceed +/- one audio slot + // (audio slot = channel count samples). + if ( feedback > audio->fb_val_max ) feedback = audio->fb_val_max; + if ( feedback < audio->fb_val_min ) feedback = audio->fb_val_min; + + tud_audio_n_fb_set(func_id, feedback); + return feedback; } -#endif -TU_ATTR_WEAK void audiod_sof (uint8_t rhport, uint32_t frame_count) +void audiod_sof_isr (uint8_t rhport, uint32_t frame_count) { (void) rhport; - (void) frame_count; // frame_count is not used since some devices may not provide the frame count value #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP - -#if (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER) - // Determine feedback value - The feedback method is described in 5.12.4.2 of the USB 2.0 spec // Boiled down, the feedback value Ff = n_samples / (micro)frame. // Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames need to be measured, where K = 10 for full speed and K = 13 for high speed, f_s is the sampling frequency e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is available and locked to f_s) @@ -2126,59 +2137,13 @@ TU_ATTR_WEAK void audiod_sof (uint8_t rhport, uint32_t frame_count) if (audio->ep_fb != 0) { - audio->fb_n_frames_current++; - if (audio->fb_n_frames_current == audio->fb_n_frames) + uint32_t const interval = (1UL << audio->fb_n_frames); + if ( 0 == (frame_count & (interval-1)) ) { - uint32_t n_cylces = *audio->fb_param_p_cycle_count; - uint32_t feedback; - -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT - feedback = (n_cylces - audio->fb_n_cycles_old) << audio->fb_power_of_two_val; -#endif - -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT - feedback = (uint32_t)((float)(n_cylces - audio->fb_n_cycles_old) * audio->fb_float_val); -#endif - -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT - feedback = ((n_cylces - audio->fb_n_cycles_old) << 3) * audio->fb_param_factor_N / audio->fb_param_factor_D; // feeback_param_factor_N has scaling factor of 13 bits, n_cycles 3 and feeback_param_factor_D 1, hence 16.16 precision -#endif - - // For Windows: https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers - // The size of isochronous packets created by the device must be within the limits specified in FMT-2.0 section 2.3.1.1. This means that the deviation of actual packet size from nominal size must not exceed +/- one audio slot (audio slot = channel count samples). - - if (feedback > audio->fb_val_max){ - feedback = audio->fb_val_max; - } - if ( feedback < audio->fb_val_min) { - feedback = audio->fb_val_min; - } - - // Buffer count checks ? - - tud_audio_n_fb_set(i, feedback); - audio->fb_n_frames_current = 0; - audio->fb_n_cycles_old = n_cylces; + if(tud_audio_sof_isr) tud_audio_sof_isr(i, frame_count); } } } - -#endif // (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER) - -#if (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_USER) - // Iterate over audio functions and call callback function - for(uint8_t i=0; i < CFG_TUD_AUDIO; i++) - { - audiod_function_t* audio = &_audiod_fct[i]; - - if (audio->ep_fb != 0) - { - tud_audio_sof_isr_cb(i, frame_count); - } - } - -#endif // (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_USER) - #endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP } @@ -2459,11 +2424,7 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER -static bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) -#else bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) -#endif { TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL); diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h index e8721cf275..9ed7d1da4d 100644 --- a/src/class/audio/audio_device.h +++ b/src/class/audio/audio_device.h @@ -191,41 +191,6 @@ #define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 0 // 0 or 1 #endif -// Possible options for determination of feedback value -#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_NO_SOF_BY_USER 1 // Feedback value must be determined by the user itself and set by use of tud_audio_n_fb_set(). The feedback value may be determined e.g. from some fill status of some FIFO buffer. Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load, disadvantage: typically a larger FIFO is needed to compensate for jitter (e.g. 8 frames), i.e. a larger delay is introduced. -#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER 2 // Feedback value is calculated within the audio driver by use of SOF interrupt. The driver needs information about the master clock f_m from which the audio sample frequency f_s is derived, f_s itself, and the cycle count of f_m at time of the SOF interrupt (e.g. by use of a hardware counter) - see tud_audio_set_fb_params(). Advantage: Reduced jitter in the feedback value computation, hence, the receive FIFO can be smaller (e.g. 2 frames) and thus a smaller delay is possible, disadvantage: higher CPU load due to SOF ISR handling every frame i.e. 1ms or 125us. This option is a great starting point to try the SOF ISR option but depending on your hardware setup (performance of the CPU) it might not work. If so, figure out why and use the next option. (The most critical point is the reading of the cycle counter value of f_m. It is read from within the SOF ISR - see: audiod_sof() -, hence, the ISR must has a high priority such that no software dependent "random" delay i.e. jitter is introduced). -#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_USER 3 // Feedback value is determined by the user by use of SOF interrupt. The user may use tud_audio_sof_isr_cb() which is called every SOF (of course only invoked when an alternate interface other than zero was set). The number of frames used to determine the feedback value for the currently active alternate setting can be get by tud_audio_get_fb_n_frames(). The feedback value must be set by use of tud_audio_n_fb_set(). - -// Determine feedback value within SOF ISR within audio driver - if disabled the user has to call tud_audio_n_fb_set() with a suitable feedback value on its own. If done within audio driver SOF ISR, tud_audio_n_fb_set() is disabled for user -#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION -#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_NO_SOF_BY_USER -#endif - -#ifdef CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION -#if (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION != CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_NO_SOF_BY_USER && CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION != CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER && CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION != CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_USER) -#error Unknown CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION. Possible choices are: CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_NO_SOF_BY_USER, CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER, or CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_USER! -#endif -#endif - -// Feeback calculation mode -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER - -#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT 1 -#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT 2 -#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT 3 - -#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE -#error You must tell the driver the feedback determination mode! Possible choices are: CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT, CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT, or CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT! -#endif - -#ifdef CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE -#if (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE != CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE != CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE != CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT) -#error Unknown CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE. Possible choices are: CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT, CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT, or CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT! -#endif -#endif - -#endif - // Audio interrupt control EP size - disabled if 0 #ifndef CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN #define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control - if required - 6 Bytes according to UAC 2 specification (p. 74) @@ -493,45 +458,14 @@ TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_byte #endif #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -TU_ATTR_WEAK bool tud_audio_fb_done_cb(uint8_t rhport); - -#if (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_NO_SOF_BY_USER) - -// This function is used to provide data rate feedback from an asynchronous sink. Feedback value will be sent at FB endpoint interval till it's changed. -// -// The feedback format is specified to be 16.16 for HS and 10.14 for FS devices (see Universal Serial Bus Specification Revision 2.0 5.12.4.2). By default, -// the choice of format is left to the caller and feedback argument is sent as-is. If CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION is set, then tinyusb -// expects 16.16 format and handles the conversion to 10.14 on FS. -// -// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0 devices. On Linux and macOS it seems the -// driver can work with either format. So a good compromise is to keep format correction disabled and stick to 16.16 format. - -// Feedback value can be determined from within the SOF ISR of the audio driver. This should reduce jitter. If the feature is used, the user can not set the feedback value. - -bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback); -static inline bool tud_audio_fb_set(uint32_t feedback); +TU_ATTR_WEAK void tud_audio_fb_done_cb(uint8_t func_id); -uint8_t tud_audio_n_get_fb_n_frames(uint8_t func_id); -static inline uint8_t tud_audio_get_fb_n_frames(); - -#endif // (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_NO_SOF_BY_USER) - -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER - -// f_m : Main clock frequency in Hz i.e. master clock to which sample clock is locked -// f_s : Current sample rate in Hz -// p_cycle_count : Pointer to main clock cycle counter register where the current count can be read from (e.g. a timer register) -// This function must be called from the user within the tud_audio_set_itf_cb(rhport, p_request) callback function, where p_request needs to be checked as -// uint8_t const itf = tu_u16_low(p_request->wIndex); -// uint8_t const alt = tu_u16_low(p_request->wValue); -// such that tud_audio_set_fb_params() gets called with the parameters corresponding to the defined interface and alternate setting -// Also, start the main clock cycle counter (or reset its value) within tud_audio_set_itf_cb() -bool tud_audio_set_fb_params(uint8_t func_id, uint32_t f_m, uint32_t f_s, uint32_t * p_cycle_count); +// determined by the user itself and set by use of tud_audio_n_fb_set(). The feedback value may be determined e.g. from some fill status of some FIFO buffer. Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load, disadvantage: typically a larger FIFO is needed to compensate for jitter (e.g. 8 frames), i.e. a larger delay is introduced. -#endif // CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_AUDIO_DRIVER +// Feedback value is calculated within the audio driver by use of SOF interrupt. The driver needs information about the master clock f_m from which the audio sample frequency f_s is derived, f_s itself, and the cycle count of f_m at time of the SOF interrupt (e.g. by use of a hardware counter) - see tud_audio_set_fb_params(). Advantage: Reduced jitter in the feedback value computation, hence, the receive FIFO can be smaller (e.g. 2 frames) and thus a smaller delay is possible, disadvantage: higher CPU load due to SOF ISR handling every frame i.e. 1ms or 125us. This option is a great starting point to try the SOF ISR option but depending on your hardware setup (performance of the CPU) it might not work. If so, figure out why and use the next option. (The most critical point is the reading of the cycle counter value of f_m. It is read from within the SOF ISR - see: audiod_sof() -, hence, the ISR must has a high priority such that no software dependent "random" delay i.e. jitter is introduced). -#if (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_USER) +// Feedback value is determined by the user by use of SOF interrupt. The user may use tud_audio_sof_isr() which is called every SOF (of course only invoked when an alternate interface other than zero was set). The number of frames used to determine the feedback value for the currently active alternate setting can be get by tud_audio_get_fb_n_frames(). The feedback value must be set by use of tud_audio_n_fb_set(). // This function is used to provide data rate feedback from an asynchronous sink. Feedback value will be sent at FB endpoint interval till it's changed. // @@ -539,20 +473,35 @@ bool tud_audio_set_fb_params(uint8_t func_id, uint32_t f_m, uint32_t f_s, uint32 // the choice of format is left to the caller and feedback argument is sent as-is. If CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION is set, then tinyusb // expects 16.16 format and handles the conversion to 10.14 on FS. // -// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0 devices. On Linux and macOS it seems the +// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0 devices. On Linux and macOS it seems the // driver can work with either format. So a good compromise is to keep format correction disabled and stick to 16.16 format. // Feedback value can be determined from within the SOF ISR of the audio driver. This should reduce jitter. If the feature is used, the user can not set the feedback value. +// Determine feedback value - The feedback method is described in 5.12.4.2 of the USB 2.0 spec +// Boiled down, the feedback value Ff = n_samples / (micro)frame. +// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames need to be measured, where K = 10 for full speed and K = 13 for high speed, f_s is the sampling frequency e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is available and locked to f_s) +// The update interval in the (4.10.2.1) Feedback Endpoint Descriptor must be less or equal to 2^(K - P), where P = min( ceil(log2(f_m / f_s)), K) +// 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); uint8_t tud_audio_n_get_fb_n_frames(uint8_t func_id); static inline uint8_t tud_audio_get_fb_n_frames(); -TU_ATTR_WEAK void tud_audio_sof_isr_cb(uint8_t func_id, uint32_t frame); +// 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 +// This function will also call tud_audio_feedback_set() +// return feedback value in 16.16 for reference (0 for error) +uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles); + +// mclk_freq : Main clock frequency in Hz i.e. master clock to which sample clock is locked +// sample_freq : sample frequency in Hz +// fixed_point : 0 float (default), 1 fixed point (for mcu without FPU) +TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, uint32_t* sample_freq, uint32_t* mclk_freq, uint8_t* fixed_point); -#endif // (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_USER) +TU_ATTR_WEAK void tud_audio_sof_isr(uint8_t func_id, uint32_t frame); #endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP @@ -695,7 +644,8 @@ static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t l } #endif -#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP && ((CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_NO_SOF_BY_USER) || (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_OPTION_SOF_BY_USER)) +#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP + static inline bool tud_audio_fb_set(uint32_t feedback) { return tud_audio_n_fb_set(0, feedback); @@ -716,7 +666,7 @@ void audiod_reset (uint8_t rhport); uint16_t audiod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); bool audiod_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes); -void audiod_sof (uint8_t rhport, uint32_t frame_count); +void audiod_sof_isr (uint8_t rhport, uint32_t frame_count); #ifdef __cplusplus } diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index bcdf8933ad..b1ee40a1a5 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -134,12 +134,6 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_offset4k(uint32_t value) { retur //------------- Mathematics -------------// TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return (v + d -1)/d; } -/// inclusive range checking TODO remove -TU_ATTR_ALWAYS_INLINE static inline bool tu_within(uint32_t lower, uint32_t value, uint32_t upper) -{ - return (lower <= value) && (value <= upper); -} - // log2 of a value is its MSB's position // TODO use clz TODO remove static inline uint8_t tu_log2(uint32_t value) diff --git a/src/device/usbd.c b/src/device/usbd.c index 8ab67c8de5..b32cee8132 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -69,7 +69,7 @@ typedef struct volatile uint8_t cfg_num; // current active configuration (0x00 is not configured) uint8_t speed; - uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid) + uint8_t itf2drv[CFG_TUD_INTERFACE_MAX]; // map interface number to driver (0xff is invalid) uint8_t ep2drv[CFG_TUD_ENDPPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each tu_edpt_state_t ep_status[CFG_TUD_ENDPPOINT_MAX][2]; @@ -134,7 +134,7 @@ static usbd_class_driver_t const _usbd_driver[] = .open = audiod_open, .control_xfer_cb = audiod_control_xfer_cb, .xfer_cb = audiod_xfer_cb, - .sof_isr = audiod_sof + .sof_isr = audiod_sof_isr }, #endif @@ -170,7 +170,7 @@ static usbd_class_driver_t const _usbd_driver[] = .open = vendord_open, .control_xfer_cb = tud_vendor_control_xfer_cb, .xfer_cb = vendord_xfer_cb, - .sof_isr = NULL + .sof_isr = NULL }, #endif diff --git a/src/device/usbd.h b/src/device/usbd.h index d485d8711a..30a27c5ee8 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -33,7 +33,7 @@ extern "C" { #endif -typedef void (*tud_sof_isr_t) (uint32_t frame_count); +// typedef void (*tud_sof_isr_t) (uint32_t frame_count); //--------------------------------------------------------------------+ // Application API From 20b810d25b60f3ab1b2ec58186bbb739b6b5c9b7 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 13 May 2022 23:01:06 +0700 Subject: [PATCH 2/7] fix ci build --- src/class/audio/audio_device.c | 7 ++++++- src/class/audio/audio_device.h | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index c3a252a2cf..a81ddc53e6 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -1708,6 +1708,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * TU_VERIFY(foundEPs == nEps); +#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP // Invoke one callback for a final set interface if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request)); @@ -1728,6 +1729,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * set_fb_params(audio, sample_freq, mclk_freq); } } +#endif // We are done - abort loop break; @@ -1737,6 +1739,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * p_desc = tu_desc_next(p_desc); } +#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP // Disable SOF interrupt if no driver has any enabled feedback EP bool disable = true; for(uint8_t i=0; i < CFG_TUD_AUDIO; i++) @@ -1748,6 +1751,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * } } if (disable) usbd_sof_enable(rhport, false); +#endif tud_control_status(rhport, p_request); @@ -2080,7 +2084,6 @@ static bool set_fb_params(audiod_function_t* audio, uint32_t f_s, uint32_t f_m) return true; } -#endif uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) { @@ -2118,10 +2121,12 @@ uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) return feedback; } +#endif void audiod_sof_isr (uint8_t rhport, uint32_t frame_count) { (void) rhport; + (void) frame_count; #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP // Determine feedback value - The feedback method is described in 5.12.4.2 of the USB 2.0 spec diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h index 9ed7d1da4d..22394aacae 100644 --- a/src/class/audio/audio_device.h +++ b/src/class/audio/audio_device.h @@ -490,6 +490,8 @@ static inline bool tud_audio_fb_set(uint32_t feedback); uint8_t tud_audio_n_get_fb_n_frames(uint8_t func_id); static inline uint8_t tud_audio_get_fb_n_frames(); +#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP + // 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 // This function will also call tud_audio_feedback_set() @@ -503,7 +505,6 @@ TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, TU_ATTR_WEAK void tud_audio_sof_isr(uint8_t func_id, uint32_t frame); -#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP #if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN TU_ATTR_WEAK bool tud_audio_int_ctr_done_cb(uint8_t rhport, uint16_t n_bytes_copied); From 5766c9ac4ed3525b3223ad0751f871b08a1bb5fb Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 19 May 2022 13:44:10 +0700 Subject: [PATCH 3/7] rename tud_audio_sof_isr() to tud_audio_feedback_interval_isr() - also add interval_log2 to isr callback - also rename other variables --- src/class/audio/audio_device.c | 37 +++++++++++++++++----------------- src/class/audio/audio_device.h | 4 +++- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index a81ddc53e6..db8bbb819c 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -326,8 +326,8 @@ typedef struct float fb_float_val; - uint32_t fb_param_factor_N; - uint32_t fb_param_factor_D; + uint32_t fb_sample_freq; + uint32_t fb_mclk_freq; #endif // CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP #endif // CFG_TUD_AUDIO_ENABLE_EP_OUT @@ -1686,11 +1686,11 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 1) // Check if usage is explicit data feedback { audio->ep_fb = ep_addr; - audio->fb_n_frames = 1 << (desc_ep->bInterval -1); + audio->fb_n_frames = 1U << (desc_ep->bInterval -1); // TODO correctly set USB frame interval for SOF (not micro-frame) audio->fb_n_frames_shift = desc_ep->bInterval -1; // Enable SOF interrupt if callback is implemented - if (tud_audio_sof_isr) usbd_sof_enable(rhport, true); + if (tud_audio_feedback_interval_isr) usbd_sof_enable(rhport, true); // // Invoke callback after ep_out is set // if (audio->ep_out != 0) @@ -2050,7 +2050,7 @@ uint8_t tud_audio_n_get_fb_n_frames(uint8_t func_id) #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -static bool set_fb_params(audiod_function_t* audio, uint32_t f_s, uint32_t f_m) +static bool set_fb_params(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq) { // Check if frame interval is within sane limits // The interval value audio->fb_n_frames was taken from the descriptors within audiod_set_interface() @@ -2058,29 +2058,29 @@ static bool set_fb_params(audiod_function_t* audio, uint32_t f_s, uint32_t f_m) // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s / f_m) for high speed // this lower limit ensures the measures feedback value has sufficient precision uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13; - if ( (((1UL << k) * f_s / f_m) + 1) > audio->fb_n_frames ) + if ( (((1UL << k) * sample_freq / mclk_freq) + 1) > audio->fb_n_frames ) { TU_LOG1(" UAC2 feedback interval too small\r\n"); TU_BREAKPOINT(); return false; } // Check if parameters really allow for a power of two division - if ((f_m % f_s) == 0 && tu_is_power_of_two(f_m / f_s)) + if ((mclk_freq % sample_freq) == 0 && tu_is_power_of_two(mclk_freq / sample_freq)) { audio->fb_compute_method = FEEDBACK_COMPUTE_POWER_OF_2; - audio->fb_power_of_two_val = 16 - audio->fb_n_frames_shift - tu_log2(f_m / f_s); + audio->fb_power_of_two_val = 16 - audio->fb_n_frames_shift - tu_log2(mclk_freq / sample_freq); }else if ( audio->fb_compute_method == FEEDBACK_COMPUTE_FLOAT) { - audio->fb_float_val = (float)f_s / f_m * (1UL << (16 - audio->fb_n_frames_shift)); + audio->fb_float_val = (float)sample_freq / mclk_freq * (1UL << (16 - audio->fb_n_frames_shift)); }else { - audio->fb_param_factor_N = f_s; - audio->fb_param_factor_D = f_m; + audio->fb_sample_freq = sample_freq; + audio->fb_mclk_freq = mclk_freq; } // 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->fb_val_min = (f_s/frame_div - 1) << 16; - audio->fb_val_max = (f_s/frame_div + 1) << 16; + audio->fb_val_min = (sample_freq/frame_div - 1) << 16; + audio->fb_val_max = (sample_freq/frame_div + 1) << 16; return true; } @@ -2093,7 +2093,7 @@ uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) switch (audio->fb_compute_method) { case FEEDBACK_COMPUTE_POWER_OF_2: - feedback = cycles << audio->fb_power_of_two_val; + feedback = (cycles << audio->fb_power_of_two_val); break; case FEEDBACK_COMPUTE_FLOAT: @@ -2102,8 +2102,8 @@ uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) case FEEDBACK_COMPUTE_FIXED: { - uint64_t fb64 = (((uint64_t) cycles) * audio->fb_param_factor_N) << (16 - audio->fb_n_frames_shift); - feedback = (uint32_t) (fb64 / audio->fb_param_factor_D); + uint64_t fb64 = (((uint64_t) cycles) * audio->fb_sample_freq) << (16 - audio->fb_n_frames_shift); + feedback = (uint32_t) (fb64 / audio->fb_mclk_freq); } break; @@ -2142,10 +2142,11 @@ void audiod_sof_isr (uint8_t rhport, uint32_t frame_count) if (audio->ep_fb != 0) { - uint32_t const interval = (1UL << audio->fb_n_frames); + // TODO hs need to be adjusted to frame (SOF event is not generated for micro-frame) + uint32_t const interval = (1UL << audio->fb_n_frames_shift); if ( 0 == (frame_count & (interval-1)) ) { - if(tud_audio_sof_isr) tud_audio_sof_isr(i, frame_count); + if(tud_audio_feedback_interval_isr) tud_audio_feedback_interval_isr(i, frame_count, audio->fb_n_frames_shift); } } } diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h index 22394aacae..e6d3b74e6c 100644 --- a/src/class/audio/audio_device.h +++ b/src/class/audio/audio_device.h @@ -503,7 +503,9 @@ uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles); // fixed_point : 0 float (default), 1 fixed point (for mcu without FPU) TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, uint32_t* sample_freq, uint32_t* mclk_freq, uint8_t* fixed_point); -TU_ATTR_WEAK void tud_audio_sof_isr(uint8_t func_id, uint32_t frame); +// Callback in ISR context, invoked periodically according to feedback endpoint bInterval. +// Could be used to compute and update feedback value +TU_ATTR_WEAK void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_log2); #if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN From e5113a1cfc25c0e5e7e75c2644ed91d252391c28 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 19 May 2022 16:05:55 +0700 Subject: [PATCH 4/7] prototype for feedback method --- src/class/audio/audio_device.c | 21 +++++++-------------- src/class/audio/audio_device.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index db8bbb819c..1dce6a6649 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -261,13 +261,6 @@ osal_mutex_def_t rx_supp_ff_mutex_rd_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO]; #endif #endif -enum { - FEEDBACK_COMPUTE_DISABLED, - FEEDBACK_COMPUTE_FLOAT, - FEEDBACK_COMPUTE_FIXED, - FEEDBACK_COMPUTE_POWER_OF_2, -}; - typedef struct { uint8_t rhport; @@ -1722,10 +1715,10 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * if ( sample_freq == 0 || mclk_freq == 0 ) { - audio->fb_compute_method = FEEDBACK_COMPUTE_DISABLED; + audio->fb_compute_method = AUDIO_FEEDBACK_METHOD_DISABLED; }else { - audio->fb_compute_method = fixed_point ? FEEDBACK_COMPUTE_FIXED : FEEDBACK_COMPUTE_FLOAT; + audio->fb_compute_method = fixed_point ? AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED : AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT; set_fb_params(audio, sample_freq, mclk_freq); } } @@ -2066,9 +2059,9 @@ static bool set_fb_params(audiod_function_t* audio, uint32_t sample_freq, uint32 // Check if parameters really allow for a power of two division if ((mclk_freq % sample_freq) == 0 && tu_is_power_of_two(mclk_freq / sample_freq)) { - audio->fb_compute_method = FEEDBACK_COMPUTE_POWER_OF_2; + audio->fb_compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2; audio->fb_power_of_two_val = 16 - audio->fb_n_frames_shift - tu_log2(mclk_freq / sample_freq); - }else if ( audio->fb_compute_method == FEEDBACK_COMPUTE_FLOAT) + }else if ( audio->fb_compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) { audio->fb_float_val = (float)sample_freq / mclk_freq * (1UL << (16 - audio->fb_n_frames_shift)); }else @@ -2092,15 +2085,15 @@ uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) switch (audio->fb_compute_method) { - case FEEDBACK_COMPUTE_POWER_OF_2: + case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2: feedback = (cycles << audio->fb_power_of_two_val); break; - case FEEDBACK_COMPUTE_FLOAT: + case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT: feedback = (uint32_t) ((float) cylces * audio->fb_float_val); break; - case FEEDBACK_COMPUTE_FIXED: + case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: { uint64_t fb64 = (((uint64_t) cycles) * audio->fb_sample_freq) << (16 - audio->fb_n_frames_shift); feedback = (uint32_t) (fb64 / audio->fb_mclk_freq); diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h index e6d3b74e6c..da4a45eeec 100644 --- a/src/class/audio/audio_device.h +++ b/src/class/audio/audio_device.h @@ -498,6 +498,34 @@ static inline uint8_t tud_audio_get_fb_n_frames(); // return feedback value in 16.16 for reference (0 for error) uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles); +enum { + AUDIO_FEEDBACK_METHOD_DISABLED, + AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED, + AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT, + AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2, + AUDIO_FEEDBACK_METHOD_FIFO_COUNT_FIXED, + AUDIO_FEEDBACK_METHOD_FIFO_COUNT_FLOAT +}; + +typedef struct { + uint8_t method; + union { + struct { + uint32_t sample_freq; + uint32_t mclk_freq; + }frequency; + + struct { + uint32_t nominal; + uint32_t threshold; + // variation etc .. + }fifo_count; + }; +}audio_feedback_params_t; + + +// TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param); + // mclk_freq : Main clock frequency in Hz i.e. master clock to which sample clock is locked // sample_freq : sample frequency in Hz // fixed_point : 0 float (default), 1 fixed point (for mcu without FPU) From 15aa593790311d6e279f01ba764a1a96d282a78b Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 27 May 2022 12:27:31 +0700 Subject: [PATCH 5/7] wrap feedback and compute to its own struct/union --- src/class/audio/audio_device.c | 132 +++++++++++++++++---------------- src/class/audio/audio_device.h | 28 ++----- 2 files changed, 78 insertions(+), 82 deletions(-) diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index 1dce6a6649..e967e8c3f8 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -306,21 +306,29 @@ typedef struct #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP - uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format). - uint8_t fb_n_frames; // Number of (micro)frames used to estimate feedback value - uint8_t fb_n_frames_shift; - uint8_t fb_compute_method; + struct { + 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. - uint32_t fb_val_min; // Maximum allowed feedback value according to UAC2 FMT-2.0 section 2.3.1.1. - uint32_t fb_val_max; // Maximum allowed feedback value according to UAC2 FMT-2.0 section 2.3.1.1. + uint8_t frame_shift; // bInterval-1 in unit of frame (FS), micro-frame (HS) + uint8_t compute_method; - // should be union - uint8_t fb_power_of_two_val; + union { + uint8_t power_of_2; // pre-computed power of 2 shift + float float_const; // pre-computed float constant - float fb_float_val; + struct { + uint32_t sample_freq; + uint32_t mclk_freq; + }fixed; - uint32_t fb_sample_freq; - uint32_t fb_mclk_freq; +// struct { +// +// }fifo_count; + }compute; + + } feedback; #endif // CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP #endif // CFG_TUD_AUDIO_ENABLE_EP_OUT @@ -437,10 +445,6 @@ static inline uint8_t tu_desc_subtype(void const* desc) } #endif -#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback); -#endif - bool tud_audio_n_mounted(uint8_t func_id) { TU_VERIFY(func_id < CFG_TUD_AUDIO); @@ -1059,7 +1063,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi #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) { - return usbd_edpt_xfer(rhport, audio->ep_fb, (uint8_t *) &audio->fb_val, 4); + return usbd_edpt_xfer(rhport, audio->ep_fb, (uint8_t *) &audio->feedback.value, 4); } #endif @@ -1564,8 +1568,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP usbd_edpt_close(rhport, audio->ep_fb); audio->ep_fb = 0; - audio->fb_n_frames = 0; - + tu_memclr(&audio->feedback, sizeof(audio->feedback)); #endif } #endif @@ -1679,8 +1682,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 1) // Check if usage is explicit data feedback { audio->ep_fb = ep_addr; - audio->fb_n_frames = 1U << (desc_ep->bInterval -1); // TODO correctly set USB frame interval for SOF (not micro-frame) - audio->fb_n_frames_shift = desc_ep->bInterval -1; + audio->feedback.frame_shift = desc_ep->bInterval -1; // Enable SOF interrupt if callback is implemented if (tud_audio_feedback_interval_isr) usbd_sof_enable(rhport, true); @@ -1708,18 +1710,26 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * // Prepare feedback computation if callback is available if (tud_audio_feedback_params_cb) { - uint32_t sample_freq = 0; - uint32_t mclk_freq = 0; - uint8_t fixed_point = 0; - tud_audio_feedback_params_cb(func_id, alt, &sample_freq, &mclk_freq, &fixed_point); + audio_feedback_params_t fb_param; - if ( sample_freq == 0 || mclk_freq == 0 ) - { - audio->fb_compute_method = AUDIO_FEEDBACK_METHOD_DISABLED; - }else + tud_audio_feedback_params_cb(func_id, alt, &fb_param); + audio->feedback.compute_method = fb_param.method; + + switch(fb_param.method) { - audio->fb_compute_method = fixed_point ? AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED : AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT; - set_fb_params(audio, sample_freq, mclk_freq); + 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.frequency.sample_freq, fb_param.frequency.mclk_freq); + break; + + case AUDIO_FEEDBACK_METHOD_FIFO_COUNT_FIXED: + case AUDIO_FEEDBACK_METHOD_FIFO_COUNT_FLOAT: + // TODO feedback by fifo count + break; + + // nothing to do + default: break; } } #endif @@ -2034,24 +2044,19 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 return false; } -#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -uint8_t tud_audio_n_get_fb_n_frames(uint8_t func_id) -{ - return _audiod_fct[func_id].fb_n_frames; -} -#endif - #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -static bool set_fb_params(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq) +static bool 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 audio->fb_n_frames was taken from the descriptors within audiod_set_interface() + // The interval value n_frames was taken from the descriptors within audiod_set_interface() // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s / f_m) for high speed // this lower limit ensures the measures feedback value has sufficient precision uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13; - if ( (((1UL << k) * sample_freq / mclk_freq) + 1) > audio->fb_n_frames ) + uint32_t const n_frame = (1UL << audio->feedback.frame_shift); + + if ( (((1UL << k) * sample_freq / mclk_freq) + 1) > n_frame ) { TU_LOG1(" UAC2 feedback interval too small\r\n"); TU_BREAKPOINT(); return false; } @@ -2059,21 +2064,23 @@ static bool set_fb_params(audiod_function_t* audio, uint32_t sample_freq, uint32 // Check if parameters really allow for a power of two division if ((mclk_freq % sample_freq) == 0 && tu_is_power_of_two(mclk_freq / sample_freq)) { - audio->fb_compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2; - audio->fb_power_of_two_val = 16 - audio->fb_n_frames_shift - tu_log2(mclk_freq / sample_freq); - }else if ( audio->fb_compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) + audio->feedback.compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2; + audio->feedback.compute.power_of_2 = 16 - audio->feedback.frame_shift - tu_log2(mclk_freq / sample_freq); + } + else if ( audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) { - audio->fb_float_val = (float)sample_freq / mclk_freq * (1UL << (16 - audio->fb_n_frames_shift)); - }else + audio->feedback.compute.float_const = (float)sample_freq / mclk_freq * (1UL << (16 - audio->feedback.frame_shift)); + } + else { - audio->fb_sample_freq = sample_freq; - audio->fb_mclk_freq = mclk_freq; + audio->feedback.compute.fixed.sample_freq = sample_freq; + audio->feedback.compute.fixed.mclk_freq = mclk_freq; } // 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->fb_val_min = (sample_freq/frame_div - 1) << 16; - audio->fb_val_max = (sample_freq/frame_div + 1) << 16; + uint32_t const frame_div = (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000; + audio->feedback.min_value = (sample_freq/frame_div - 1) << 16; + audio->feedback.max_value = (sample_freq/frame_div + 1) << 16; return true; } @@ -2083,20 +2090,20 @@ uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) audiod_function_t* audio = &_audiod_fct[func_id]; uint32_t feedback; - switch (audio->fb_compute_method) + switch (audio->feedback.compute_method) { case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2: - feedback = (cycles << audio->fb_power_of_two_val); + feedback = (cycles << audio->feedback.compute.power_of_2); break; case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT: - feedback = (uint32_t) ((float) cylces * audio->fb_float_val); + feedback = (uint32_t) ((float) cycles * audio->feedback.compute.float_const); break; case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: { - uint64_t fb64 = (((uint64_t) cycles) * audio->fb_sample_freq) << (16 - audio->fb_n_frames_shift); - feedback = (uint32_t) (fb64 / audio->fb_mclk_freq); + uint64_t fb64 = (((uint64_t) cycles) * audio->feedback.compute.fixed.sample_freq) << (16 - audio->feedback.frame_shift); + feedback = (uint32_t) (fb64 / audio->feedback.compute.fixed.mclk_freq); } break; @@ -2107,8 +2114,8 @@ uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) // The size of isochronous packets created by the device must be within the limits specified in FMT-2.0 section 2.3.1.1. // This means that the deviation of actual packet size from nominal size must not exceed +/- one audio slot // (audio slot = channel count samples). - if ( feedback > audio->fb_val_max ) feedback = audio->fb_val_max; - if ( feedback < audio->fb_val_min ) feedback = audio->fb_val_min; + if ( feedback > audio->feedback.max_value ) feedback = audio->feedback.max_value; + if ( feedback < audio->feedback.min_value ) feedback = audio->feedback.min_value; tud_audio_n_fb_set(func_id, feedback); @@ -2135,11 +2142,12 @@ void audiod_sof_isr (uint8_t rhport, uint32_t frame_count) if (audio->ep_fb != 0) { - // TODO hs need to be adjusted to frame (SOF event is not generated for micro-frame) - uint32_t const interval = (1UL << audio->fb_n_frames_shift); + // HS shift need to be adjusted since SOF event is generated for frame only + uint8_t const hs_adjust = (TUSB_SPEED_HIGH == tud_speed_get()) ? 3 : 0; + uint32_t const interval = 1UL << (audio->feedback.frame_shift - hs_adjust); if ( 0 == (frame_count & (interval-1)) ) { - if(tud_audio_feedback_interval_isr) tud_audio_feedback_interval_isr(i, frame_count, audio->fb_n_frames_shift); + if(tud_audio_feedback_interval_isr) tud_audio_feedback_interval_isr(i, frame_count, audio->feedback.frame_shift); } } } @@ -2431,7 +2439,7 @@ bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION if ( TUSB_SPEED_FULL == tud_speed_get() ) { - uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].fb_val; + uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].feedback.value; // For FS format is 10.14 *(fb++) = (feedback >> 2) & 0xFF; @@ -2443,7 +2451,7 @@ bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) #else { // Send value as-is, caller will choose the appropriate format - _audiod_fct[func_id].fb_val = feedback; + _audiod_fct[func_id].feedback.value = feedback; } #endif diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h index da4a45eeec..09de8e9a80 100644 --- a/src/class/audio/audio_device.h +++ b/src/class/audio/audio_device.h @@ -487,11 +487,6 @@ TU_ATTR_WEAK void tud_audio_fb_done_cb(uint8_t func_id); bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback); static inline bool tud_audio_fb_set(uint32_t feedback); -uint8_t tud_audio_n_get_fb_n_frames(uint8_t func_id); -static inline uint8_t tud_audio_get_fb_n_frames(); - -#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP - // 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 // This function will also call tud_audio_feedback_set() @@ -511,8 +506,8 @@ typedef struct { uint8_t method; union { struct { - uint32_t sample_freq; - uint32_t mclk_freq; + uint32_t sample_freq; // sample frequency in Hz + uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to which sample clock is based on }frequency; struct { @@ -523,18 +518,16 @@ typedef struct { }; }audio_feedback_params_t; - -// TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param); - -// mclk_freq : Main clock frequency in Hz i.e. master clock to which sample clock is locked -// sample_freq : sample frequency in Hz -// fixed_point : 0 float (default), 1 fixed point (for mcu without FPU) -TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, uint32_t* sample_freq, uint32_t* mclk_freq, uint8_t* fixed_point); +// Invoked when needed to set feedback parameters +TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param); // Callback in ISR context, invoked periodically according to feedback endpoint bInterval. // Could be used to compute and update feedback value -TU_ATTR_WEAK void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_log2); +// frame_number : current SOF count +// interval_shift: number of bit shift i.e log2(interval) from Feedback endpoint descriptor +TU_ATTR_WEAK void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift); +#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP #if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN TU_ATTR_WEAK bool tud_audio_int_ctr_done_cb(uint8_t rhport, uint16_t n_bytes_copied); @@ -682,11 +675,6 @@ static inline bool tud_audio_fb_set(uint32_t feedback) return tud_audio_n_fb_set(0, feedback); } -static inline uint8_t tud_audio_get_fb_n_frames() -{ - return tud_audio_n_get_fb_n_frames(0); -} - #endif //--------------------------------------------------------------------+ From dce2ad4ffb33fe884429e0c241b1957b3cd6c453 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 27 May 2022 23:11:25 +0700 Subject: [PATCH 6/7] adding feedback fifo count (WIP) --- src/class/audio/audio_device.c | 47 +++++++++++++++++++--------------- src/class/audio/audio_device.h | 9 +++---- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index e967e8c3f8..31851e00c9 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -323,9 +323,10 @@ typedef struct uint32_t mclk_freq; }fixed; -// struct { -// -// }fifo_count; + struct { + uint32_t nominal_value; + uint32_t threshold_bytes; + }fifo_count; }compute; } feedback; @@ -1715,17 +1716,28 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * tud_audio_feedback_params_cb(func_id, alt, &fb_param); audio->feedback.compute_method = fb_param.method; + // 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.max_value = (fb_param.sample_freq/frame_div + 1) << 16; + switch(fb_param.method) { 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.frequency.sample_freq, fb_param.frequency.mclk_freq); + set_fb_params_freq(audio, fb_param.sample_freq, fb_param.frequency.mclk_freq); break; case AUDIO_FEEDBACK_METHOD_FIFO_COUNT_FIXED: case AUDIO_FEEDBACK_METHOD_FIFO_COUNT_FLOAT: - // TODO feedback by 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); + } break; // nothing to do @@ -1972,14 +1984,14 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 (void) xferred_bytes; // Search for interface belonging to given end point address and proceed as required - uint8_t func_id; - for (func_id = 0; func_id < CFG_TUD_AUDIO; func_id++) + for (uint8_t func_id = 0; func_id < CFG_TUD_AUDIO; func_id++) { + audiod_function_t* audio = &_audiod_fct[func_id]; #if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN // Data transmission of control interrupt finished - if (_audiod_fct[func_id].ep_int_ctr == ep_addr) + if (audio->ep_int_ctr == ep_addr) { // According to USB2 specification, maximum payload of interrupt EP is 8 bytes on low speed, 64 bytes on full speed, and 1024 bytes on high speed (but only if an alternate interface other than 0 is used - see specification p. 49) // In case there is nothing to send we have to return a NAK - this is taken care of by PHY ??? @@ -1996,7 +2008,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 #if CFG_TUD_AUDIO_ENABLE_EP_IN // Data transmission of audio packet finished - if (_audiod_fct[func_id].ep_in == ep_addr && _audiod_fct[func_id].alt_setting != 0) + if (audio->ep_in == ep_addr && audio->alt_setting != 0) { // USB 2.0, section 5.6.4, third paragraph, states "An isochronous endpoint must specify its required bus access period. However, an isochronous endpoint must be prepared to handle poll rates faster than the one specified." // That paragraph goes on to say "An isochronous IN endpoint must return a zero-length packet whenever data is requested at a faster interval than the specified interval and data is not available." @@ -2007,7 +2019,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 // This is the only place where we can fill something into the EPs buffer! // Load new data - TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_fct[func_id])); + TU_VERIFY(audiod_tx_done_cb(rhport, audio)); // Transmission of ZLP is done by audiod_tx_done_cb() return true; @@ -2017,24 +2029,24 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 #if CFG_TUD_AUDIO_ENABLE_EP_OUT // New audio packet received - if (_audiod_fct[func_id].ep_out == ep_addr) + if (audio->ep_out == ep_addr) { - TU_VERIFY(audiod_rx_done_cb(rhport, &_audiod_fct[func_id], (uint16_t) xferred_bytes)); + TU_VERIFY(audiod_rx_done_cb(rhport, audio, (uint16_t) xferred_bytes)); return true; } #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP // Transmission of feedback EP finished - if (_audiod_fct[func_id].ep_fb == ep_addr) + if (audio->ep_fb == ep_addr) { if (tud_audio_fb_done_cb) tud_audio_fb_done_cb(func_id); // Schedule a transmit with the new value if EP is not busy - if (!usbd_edpt_busy(rhport, _audiod_fct[func_id].ep_fb)) + 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, &_audiod_fct[func_id]); + return audiod_fb_send(rhport, audio); } } #endif @@ -2077,11 +2089,6 @@ static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, u audio->feedback.compute.fixed.mclk_freq = mclk_freq; } - // 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 = (sample_freq/frame_div - 1) << 16; - audio->feedback.max_value = (sample_freq/frame_div + 1) << 16; - return true; } diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h index 09de8e9a80..c44b4cc301 100644 --- a/src/class/audio/audio_device.h +++ b/src/class/audio/audio_device.h @@ -504,16 +504,15 @@ enum { typedef struct { uint8_t method; + uint32_t sample_freq; // sample frequency in Hz + union { struct { - uint32_t sample_freq; // sample frequency in Hz - uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to which sample clock is based on + uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to which sample clock is based on }frequency; struct { - uint32_t nominal; - uint32_t threshold; - // variation etc .. + uint32_t threshold_bytes; // minimum number of bytes received to be considered as filled/ready }fifo_count; }; }audio_feedback_params_t; From f2926670cc4e495e025b0838fed5b44d27e4cd43 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 31 May 2022 20:26:37 +0700 Subject: [PATCH 7/7] comment out fifo count method for now --- src/class/audio/audio_device.c | 18 ++++++++++-------- src/class/audio/audio_device.h | 7 +++++-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index 31851e00c9..01a42f34fe 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -305,13 +305,12 @@ typedef struct #endif #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP - struct { - 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. + 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. - uint8_t frame_shift; // bInterval-1 in unit of frame (FS), micro-frame (HS) + uint8_t frame_shift; // bInterval-1 in unit of frame (FS), micro-frame (HS) uint8_t compute_method; union { @@ -323,15 +322,17 @@ typedef struct uint32_t mclk_freq; }fixed; +#if 0 // implement later struct { uint32_t nominal_value; uint32_t threshold_bytes; }fifo_count; +#endif }compute; } feedback; - #endif // CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP + #endif // CFG_TUD_AUDIO_ENABLE_EP_OUT #if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING @@ -1729,8 +1730,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * set_fb_params_freq(audio, fb_param.sample_freq, fb_param.frequency.mclk_freq); break; - case AUDIO_FEEDBACK_METHOD_FIFO_COUNT_FIXED: - case AUDIO_FEEDBACK_METHOD_FIFO_COUNT_FLOAT: + #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); @@ -1739,6 +1740,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * tud_audio_fb_set(audio->feedback.compute.fifo_count.nominal_value); } break; + #endif // nothing to do default: break; diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h index c44b4cc301..13f24fb624 100644 --- a/src/class/audio/audio_device.h +++ b/src/class/audio/audio_device.h @@ -498,8 +498,9 @@ enum { AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED, AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT, AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2, - AUDIO_FEEDBACK_METHOD_FIFO_COUNT_FIXED, - AUDIO_FEEDBACK_METHOD_FIFO_COUNT_FLOAT + + // impelemnt later + // AUDIO_FEEDBACK_METHOD_FIFO_COUNT }; typedef struct { @@ -511,9 +512,11 @@ 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;