From 759d5305062e5261d580fa64e40fc755db34c436 Mon Sep 17 00:00:00 2001 From: Jerzy Kasenberg Date: Fri, 11 Sep 2020 13:42:52 +0200 Subject: [PATCH] audio_device: Allow one FIFO for N channels This allow to build with single FIFO for devices with multiple channels. Having just one FIFO greatly reduces time needed to feed endpoint. This change also allows to have one FIFO with 24 bit samples that is not rounded up to 32 bit elements. CFG_TUD_AUDIO_RX_ITEMSIZE and CFG_TUD_AUDIO_TX_ITEMSIZE can be manually defined. This allows to use FIFO more efficiently when 24 bits samples are already using 3 bytes, in this case there is no need to put them into FIFO one by one. For 8, 16, 32 bits samples size efficient FIFO access is always used when single FIFO is selected. This also changes FIFO element size to 1, FIFO usage was confusing in some place it treated content as byte base in other it looked like ITEM size is to be used. Also bufsize that in most (maybe all) cases was really meaning item count. bufsize now mean buffer size in bytes so there is no confusion. --- src/class/audio/audio_device.c | 147 +++++++++++++++++++++++++++------ src/class/audio/audio_device.h | 60 +++++++++++++- 2 files changed, 179 insertions(+), 28 deletions(-) diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index 2660cf36..181703f7 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -46,6 +46,19 @@ //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ + +#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE +#ifndef CFG_TUD_AUDIO_TX_FIFO_COUNT +#define CFG_TUD_AUDIO_TX_FIFO_COUNT CFG_TUD_AUDIO_N_CHANNELS_TX +#endif +#endif + +#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE +#ifndef CFG_TUD_AUDIO_RX_FIFO_COUNT +#define CFG_TUD_AUDIO_RX_FIFO_COUNT CFG_TUD_AUDIO_N_CHANNELS_RX +#endif +#endif + typedef struct { uint8_t rhport; @@ -81,18 +94,18 @@ typedef struct // FIFO #if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE - tu_fifo_t tx_ff[CFG_TUD_AUDIO_N_CHANNELS_TX]; - CFG_TUSB_MEM_ALIGN uint8_t tx_ff_buf[CFG_TUD_AUDIO_N_CHANNELS_TX][CFG_TUD_AUDIO_TX_FIFO_SIZE * CFG_TUD_AUDIO_TX_ITEMSIZE]; + tu_fifo_t tx_ff[CFG_TUD_AUDIO_TX_FIFO_COUNT]; + CFG_TUSB_MEM_ALIGN uint8_t tx_ff_buf[CFG_TUD_AUDIO_TX_FIFO_COUNT][CFG_TUD_AUDIO_TX_FIFO_SIZE]; #if CFG_FIFO_MUTEX - osal_mutex_def_t tx_ff_mutex[CFG_TUD_AUDIO_N_CHANNELS_TX]; + osal_mutex_def_t tx_ff_mutex[CFG_TUD_AUDIO_TX_FIFO_COUNT]; #endif #endif #if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE - tu_fifo_t rx_ff[CFG_TUD_AUDIO_N_CHANNELS_RX]; - CFG_TUSB_MEM_ALIGN uint8_t rx_ff_buf[CFG_TUD_AUDIO_N_CHANNELS_RX][CFG_TUD_AUDIO_RX_FIFO_SIZE * CFG_TUD_AUDIO_RX_ITEMSIZE]; + tu_fifo_t rx_ff[CFG_TUD_AUDIO_RX_FIFO_COUNT]; + CFG_TUSB_MEM_ALIGN uint8_t rx_ff_buf[CFG_TUD_AUDIO_RX_FIFO_COUNT][CFG_TUD_AUDIO_RX_FIFO_SIZE]; #if CFG_FIFO_MUTEX - osal_mutex_def_t rx_ff_mutex[CFG_TUD_AUDIO_N_CHANNELS_RX]; + osal_mutex_def_t rx_ff_mutex[CFG_TUD_AUDIO_RX_FIFO_COUNT]; #endif #endif @@ -190,7 +203,7 @@ bool tud_audio_n_mounted(uint8_t itf) //--------------------------------------------------------------------+ #if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE - +#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1 uint16_t tud_audio_n_available(uint8_t itf, uint8_t channelId) { TU_VERIFY(channelId < CFG_TUD_AUDIO_N_CHANNELS_RX); @@ -208,7 +221,22 @@ void tud_audio_n_read_flush (uint8_t itf, uint8_t channelId) TU_VERIFY(channelId < CFG_TUD_AUDIO_N_CHANNELS_RX, ); tu_fifo_clear(&_audiod_itf[itf].rx_ff[channelId]); } +#else +uint16_t tud_audio_n_available(uint8_t itf) +{ + return tu_fifo_count(&_audiod_itf[itf].rx_ff[0]); +} +uint16_t tud_audio_n_read(uint8_t itf, void* buffer, uint16_t bufsize) +{ + return tu_fifo_read_n(&_audiod_itf[itf].rx_ff[0], buffer, bufsize); +} + +void tud_audio_n_read_flush (uint8_t itf) +{ + tu_fifo_clear(&_audiod_itf[itf].rx_ff[0]); +} +#endif #endif #if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN @@ -283,6 +311,7 @@ static bool audio_rx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t* // The following functions are used in case CFG_TUD_AUDIO_RX_FIFO_SIZE != 0 #if CFG_TUD_AUDIO_RX_FIFO_SIZE +#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1 static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t * buffer, uint16_t bufsize) { (void) rhport; @@ -318,7 +347,23 @@ static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* a chId = 0; } } -} } + return true; +} +#else +static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t *audio, uint8_t *buffer, uint16_t bufsize) +{ + (void) rhport; + + // We expect to get a multiple of CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX per channel + if (bufsize % (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX) != 0) + { + return false; + } + + tu_fifo_write_n(&audio->rx_ff[0], buffer, bufsize); + return true; +} +#endif // CFG_TUD_AUDIO_RX_FIFO_COUNT > 1 #endif //CFG_TUD_AUDIO_RX_FIFO_SIZE //--------------------------------------------------------------------+ @@ -336,9 +381,9 @@ static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* a * \param[in] len: # of array elements to copy * \return Number of bytes actually written */ - +#if CFG_TUD_AUDIO_EPSIZE_IN +#if !CFG_TUD_AUDIO_TX_FIFO_SIZE /* This function is intended for later use once EP buffers (at least for ISO EPs) are implemented as ring buffers -#if CFG_TUD_AUDIO_EPSIZE_IN && !CFG_TUD_AUDIO_TX_FIFO_SIZE uint16_t tud_audio_n_write_ep_in_buffer(uint8_t itf, const void * data, uint16_t len) { audiod_interface_t* audio = &_audiod_itf[itf]; @@ -365,11 +410,23 @@ uint16_t tud_audio_n_write_ep_in_buffer(uint8_t itf, const void * data, uint16_t // Return number of bytes written return len; } -#endif - */ -#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE +#else + +#if CFG_TUD_AUDIO_TX_FIFO_COUNT == 1 +uint16_t tud_audio_n_write(uint8_t itf, void const* data, uint16_t len) +{ + { + audiod_interface_t* audio = &_audiod_itf[itf]; + if (audio->p_desc == NULL) + { + return 0; + } + return tu_fifo_write_n(&audio->tx_ff[0], data, len); + } +} +#else uint16_t tud_audio_n_write(uint8_t itf, uint8_t channelId, const void * data, uint16_t len) { audiod_interface_t* audio = &_audiod_itf[itf]; @@ -381,6 +438,23 @@ uint16_t tud_audio_n_write(uint8_t itf, uint8_t channelId, const void * data, ui } #endif +static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_t * n_bytes_copied); + +uint16_t tud_audio_n_write_flush(uint8_t itf) +{ + audiod_interface_t *audio = &_audiod_itf[itf]; + if (audio->p_desc == NULL) { + return 0; + } + + uint16_t n_bytes_copied; + TU_VERIFY(audiod_tx_done_cb(audio->rhport, audio, &n_bytes_copied)); + return n_bytes_copied; +} + +#endif +#endif + #if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0 uint32_t tud_audio_int_ctr_n_write(uint8_t itf, uint8_t const* buffer, uint32_t bufsize) { @@ -477,6 +551,7 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_ #endif //CFG_TUD_AUDIO_EPSIZE_IN #if CFG_TUD_AUDIO_TX_FIFO_SIZE +#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1 || (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX != CFG_TUD_AUDIO_TX_ITEMSIZE) static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio) { // We encode directly into IN EP's buffer - abort if previous transfer not complete @@ -484,15 +559,15 @@ static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* // Determine amount of samples uint16_t const nEndpointSampleCapacity = CFG_TUD_AUDIO_EPSIZE_IN / CFG_TUD_AUDIO_N_CHANNELS_TX / CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX; - uint16_t nSamplesPerChannelToSend = tu_fifo_count(&audio->tx_ff[0]); + uint16_t nSamplesPerChannelToSend = tu_fifo_count(&audio->tx_ff[0]) / CFG_TUD_AUDIO_TX_ITEMSIZE; uint16_t nBytesToSend; uint8_t cntChannel; for (cntChannel = 1; cntChannel < CFG_TUD_AUDIO_N_CHANNELS_TX; cntChannel++) { - if (audio->tx_ff[cntChannel].count < nSamplesPerChannelToSend) + if (audio->tx_ff[cntChannel].count / CFG_TUD_AUDIO_TX_ITEMSIZE < nSamplesPerChannelToSend) { - nSamplesPerChannelToSend = audio->tx_ff[cntChannel].count; + nSamplesPerChannelToSend = audio->tx_ff[cntChannel].count * CFG_TUD_AUDIO_TX_ITEMSIZE; } } @@ -524,7 +599,7 @@ static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* for (cntChannel = 0; cntChannel < CFG_TUD_AUDIO_N_CHANNELS_TX; cntChannel++) { // Get sample from buffer - tu_fifo_read(&audio->tx_ff[cntChannel], &sample); + tu_fifo_read_n(&audio->tx_ff[cntChannel], &sample, CFG_TUD_AUDIO_TX_ITEMSIZE); // Put it into EP's buffer - Let alignment problems be handled by memcpy memcpy(pBuff, &sample, CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX); @@ -539,6 +614,30 @@ static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* return true; } +#else +static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio) +{ + // We encode directly into IN EP's buffer - abort if previous transfer not complete + TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_in)); + + // Determine amount of samples + uint16_t nByteCount = tu_fifo_count(&audio->tx_ff[0]); + + nByteCount = tu_min16(nByteCount, CFG_TUD_AUDIO_EPSIZE_IN); + + // Check if there is enough + if (nByteCount == 0) + { + return true; + } + + nByteCount = tu_fifo_read_n(&audio->tx_ff[0], audio->epin_buf, nByteCount); + audio->epin_buf_cnt = nByteCount; + + return true; +} +#endif // CFG_TUD_AUDIO_TX_FIFO_COUNT > 1 || (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX != CFG_TUD_AUDIO_TX_ITEMSIZE) + #endif //CFG_TUD_AUDIO_TX_FIFO_SIZE // This function is called once a transmit of an feedback packet was successfully completed. Here, we get the next feedback value to be sent @@ -588,7 +687,6 @@ static bool audio_int_ctr_done_cb(uint8_t rhport, audiod_interface_t* audio, uin //--------------------------------------------------------------------+ void audiod_init(void) { - uint8_t cnt; tu_memclr(_audiod_itf, sizeof(_audiod_itf)); for(uint8_t i=0; itx_ff[cnt], &audio->tx_ff_buf[cnt], CFG_TUD_AUDIO_TX_FIFO_SIZE, CFG_TUD_AUDIO_TX_ITEMSIZE, true); + tu_fifo_config(&audio->tx_ff[cnt], &audio->tx_ff_buf[cnt], CFG_TUD_AUDIO_TX_FIFO_SIZE, 1, true); #if CFG_FIFO_MUTEX tu_fifo_config_mutex(&audio->tx_ff[cnt], osal_mutex_create(&audio->tx_ff_mutex[cnt])); #endif @@ -607,9 +705,9 @@ void audiod_init(void) #endif #if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE - for (cnt = 0; cnt < CFG_TUD_AUDIO_N_CHANNELS_RX; cnt++) + for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_RX_FIFO_COUNT; cnt++) { - tu_fifo_config(&audio->rx_ff[cnt], &audio->rx_ff_buf[cnt], CFG_TUD_AUDIO_RX_FIFO_SIZE, CFG_TUD_AUDIO_RX_ITEMSIZE, true); + tu_fifo_config(&audio->rx_ff[cnt], &audio->rx_ff_buf[cnt], CFG_TUD_AUDIO_RX_FIFO_SIZE, 1, true); #if CFG_FIFO_MUTEX tu_fifo_config_mutex(&audio->rx_ff[cnt], osal_mutex_create(&audio->rx_ff_mutex[cnt])); #endif @@ -634,16 +732,15 @@ void audiod_reset(uint8_t rhport) audiod_interface_t* audio = &_audiod_itf[i]; tu_memclr(audio, ITF_MEM_RESET_SIZE); - uint8_t cnt; #if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE - for (cnt = 0; cnt < CFG_TUD_AUDIO_N_CHANNELS_TX; cnt++) + for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_TX_FIFO_COUNT; cnt++) { tu_fifo_clear(&audio->tx_ff[cnt]); } #endif #if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE - for (cnt = 0; cnt < CFG_TUD_AUDIO_N_CHANNELS_RX; cnt++) + for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_RX_FIFO_COUNT; cnt++) { tu_fifo_clear(&audio->rx_ff[cnt]); } diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h index 2adb9525..2f48955f 100644 --- a/src/class/audio/audio_device.h +++ b/src/class/audio/audio_device.h @@ -119,6 +119,7 @@ #define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX 1 #endif +#ifndef CFG_TUD_AUDIO_TX_ITEMSIZE #if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 1 #define CFG_TUD_AUDIO_TX_ITEMSIZE 1 #elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 2 @@ -126,6 +127,11 @@ #else #define CFG_TUD_AUDIO_TX_ITEMSIZE 4 #endif +#endif + +#if CFG_TUD_AUDIO_TX_ITEMSIZE < CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX +#error FIFO element size (ITEMSIZE) must not be smaller then sample size +#endif #endif @@ -170,9 +176,15 @@ extern "C" { bool tud_audio_n_mounted (uint8_t itf); #if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE +#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1 uint16_t tud_audio_n_available (uint8_t itf, uint8_t channelId); uint16_t tud_audio_n_read (uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize); void tud_audio_n_read_flush (uint8_t itf, uint8_t channelId); +#else +uint16_t tud_audio_n_available (uint8_t itf); +uint16_t tud_audio_n_read (uint8_t itf, void* buffer, uint16_t bufsize); +void tud_audio_n_read_flush (uint8_t itf); +#endif #endif /* This function is intended for later use once EP buffers (at least for ISO EPs) are implemented as ring buffers @@ -182,7 +194,12 @@ uint16_t tud_audio_n_write_ep_in_buffer(uint8_t itf, const void * data, uint16_t */ #if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE +#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1 uint16_t tud_audio_n_write (uint8_t itf, uint8_t channelId, const void * data, uint16_t len); +#else +uint16_t tud_audio_n_write (uint8_t itf, const void * data, uint16_t len); +#endif +uint16_t tud_audio_n_write_flush(uint8_t itf); #endif #if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0 @@ -205,7 +222,11 @@ static inline void tud_audio_read_flush (void); #endif #if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE +#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1 static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t bufsize); +#else +static inline uint16_t tud_audio_write (uint8_t const* buffer, uint16_t bufsize); +#endif #endif #if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0 @@ -274,14 +295,31 @@ static inline bool tud_audio_mounted(void) return tud_audio_n_mounted(0); } -#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE -static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t bufsize) // Short version if only one audio function is used +#if CFG_TUD_AUDIO_EPSIZE_IN +#if CFG_TUD_AUDIO_TX_FIFO_SIZE && CFG_TUD_AUDIO_TX_FIFO_COUNT > 1 +static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t n_bytes) // Short version if only one audio function is used { - return tud_audio_n_write(0, channelId, buffer, bufsize); + return tud_audio_n_write(0, channelId, buffer, n_bytes); +} +#else +static inline uint16_t tud_audio_write (uint8_t const* buffer, uint16_t n_bytes) // Short version if only one audio function is used +{ + return tud_audio_n_write(0, buffer, n_bytes); +} +#endif + +static inline uint16_t tud_audio_write_flush (void) // Short version if only one audio function is used +{ +#if CFG_TUD_AUDIO_TX_FIFO_SIZE + return tud_audio_n_write_flush(0); +#else + return 0; +#endif } #endif // CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE #if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE +#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1 static inline uint16_t tud_audio_available(uint8_t channelId) { return tud_audio_n_available(0, channelId); @@ -296,6 +334,22 @@ static inline void tud_audio_read_flush(uint8_t channelId) { tud_audio_n_read_flush(0, channelId); } +#else +static inline uint16_t tud_audio_available(void) +{ + return tud_audio_n_available(0); +} + +static inline uint16_t tud_audio_read(void *buffer, uint16_t bufsize) +{ + return tud_audio_n_read(0, buffer, bufsize); +} + +static inline void tud_audio_read_flush(void) +{ + tud_audio_n_read_flush(0); +} +#endif #endif #if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0