diff options
| author | HiFiPHile <[email protected]> | 2026-08-25 09:28:12 +0200 |
|---|---|---|
| committer | HiFiPHile <[email protected]> | 2026-08-25 09:28:12 +0200 |
| commit | ddf4bad89017dab131eddf2fa27d835218e7f72e (patch) | |
| tree | e0d3fe77802c9046a811227b15604644a8454728 /src/class | |
| parent | dfac26a272fa7bbbca2050fbe9f1ca09008e548e (diff) | |
fix(audio): drive streams from transfer completion
Keep capture and playback transfers continuously armed from their completion callbacks. Capture overwrites the oldest complete frames when full, while playback sends silence on underrun without consuming partial queued audio.
Signed-off-by: HiFiPHile <[email protected]>
Diffstat (limited to 'src/class')
| -rw-r--r-- | src/class/audio/audio_host.c | 36 | ||||
| -rw-r--r-- | src/class/audio/audio_host.h | 6 |
2 files changed, 21 insertions, 21 deletions
diff --git a/src/class/audio/audio_host.c b/src/class/audio/audio_host.c index 8918ca86e..b682d2d8a 100644 --- a/src/class/audio/audio_host.c +++ b/src/class/audio/audio_host.c @@ -311,15 +311,12 @@ static tuh_audio_stream_t *audioh_find_stream(uint8_t dev_addr, uint8_t ep_addr) //--------------------------------------------------------------------+ // Re-arm the capture endpoint: request one full packet (the device sends at -// most its max packet size per poll interval). Only submit while the whole -// packet fits into the FIFO — otherwise the frame is lost anyway and the -// transfer would be wasted; the stream resumes when tuh_audio_read() frees -// FIFO space. +// most its max packet size per poll interval). The overwritable FIFO retains +// the newest capture frames when the application cannot drain it in time. static void audioh_stream_capture_xfer(tuh_audio_stream_t *s) { TU_VERIFY(s->state == STREAM_STATE_READY && s->running, ); const audioh_stream_map_t *map = &s->map[s->active_config]; - TU_VERIFY(tu_fifo_remaining(&s->edpt.ff) >= map->ep_size, ); TU_VERIFY(usbh_edpt_claim(s->daddr, map->ep_addr), ); // one transfer in flight // ep_size is guaranteed <= CFG_TUH_AUDIO_EPIN_BUFSIZE by enumeration @@ -347,13 +344,13 @@ static void audioh_stream_playback_xfer(tuh_audio_stream_t *s) { bytes_64 <= CFG_TUH_AUDIO_STREAM_BUFSIZE, ); const uint16_t bytes = (uint16_t)bytes_64; if (tu_fifo_count(&s->edpt.ff) < bytes) { - // Wait until one complete poll interval is queued. This is required when - // bInterval is greater than one frame and also avoids short audio packets. - usbh_edpt_release(s->daddr, map->ep_addr); - return; + // Keep the isochronous stream active without consuming a partial frame. + // The queued audio is sent once a complete poll interval is available. + tu_memclr(s->edpt.ep_buf, bytes); + } else { + tu_fifo_read_n(&s->edpt.ff, s->edpt.ep_buf, bytes); } - tu_fifo_read_n(&s->edpt.ff, s->edpt.ep_buf, bytes); TU_ASSERT(usbh_edpt_xfer(s->daddr, map->ep_addr, s->edpt.ep_buf, bytes), ); s->rem_acc = next_rem_acc; } @@ -506,7 +503,7 @@ bool audioh_init(void) { out->dir = TUSB_DIR_OUT; // Bind FIFO buffer and transfer buffer (see tu_edpt_stream_init) - TU_VERIFY(tu_edpt_stream_init(&in->edpt, true, false, false, in->ff_buf, CFG_TUH_AUDIO_STREAM_BUFSIZE, + TU_VERIFY(tu_edpt_stream_init(&in->edpt, true, false, true, in->ff_buf, CFG_TUH_AUDIO_STREAM_BUFSIZE, _audioh_epbuf[idx].epin)); TU_VERIFY(tu_edpt_stream_init(&out->edpt, true, true, false, out->ff_buf, CFG_TUH_AUDIO_STREAM_BUFSIZE, _audioh_epbuf[idx].epout)); @@ -883,8 +880,8 @@ uint16_t audioh_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface uint8_t usb_input_terminal_id = 0; uint8_t usb_output_source_id = 0; // A Feature Unit may precede the USB terminal that identifies its stream. - uint8_t pending_fu_id = 0; - uint8_t pending_fu_source_id = 0; + uint8_t pending_fu_id = 0; + uint8_t pending_fu_source_id = 0; bool have_header = false; p_desc = tu_desc_next(p_desc); @@ -1159,6 +1156,12 @@ bool tuh_audio_configure(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_idx s->complete_cb = complete_cb; s->user_data = user_data; s->state = STREAM_STATE_CONFIG; + if (s->dir == TUSB_DIR_IN) { + // A byte FIFO can overwrite only complete audio frames when its depth is + // an exact multiple of the configured frame size. + const uint16_t fifo_depth = CFG_TUH_AUDIO_STREAM_BUFSIZE - (CFG_TUH_AUDIO_STREAM_BUFSIZE % s->frame_bytes); + TU_VERIFY(tu_fifo_config(&s->edpt.ff, s->ff_buf, fifo_depth, true), false); + } TU_LOG_DRV(" AUDIO configure %s stream %u: itf %u alt %u ep %02x\r\n", (s->dir == TUSB_DIR_IN) ? "capture" : "playback", s->stream_idx, map->itf_num, map->alt_setting, @@ -1187,7 +1190,7 @@ static void audioh_stream_start_complete(tuh_xfer_t *xfer) { if (s->dir == TUSB_DIR_IN) { audioh_stream_capture_xfer(s); // feed the capture endpoint } else { - audioh_stream_playback_xfer(s); // flush queued frames, if any + audioh_stream_playback_xfer(s); // start the continuous playback transfer chain } } @@ -1261,10 +1264,6 @@ uint32_t tuh_audio_write(uint8_t dev_idx, uint8_t stream_idx, const void *buffer } tu_fifo_write_n(&s->edpt.ff, buffer, (uint16_t)(frames * s->frame_bytes)); - // Flush a packet when the FIFO holds at least one; the scheduler drains - // the rest on completion - audioh_stream_playback_xfer(s); - return frames; } @@ -1283,7 +1282,6 @@ uint32_t tuh_audio_read(uint8_t dev_idx, uint8_t stream_idx, void *buffer, uint3 const uint32_t frames = TU_MIN(frame_count, tu_fifo_count(&s->edpt.ff) / s->frame_bytes); if (frames > 0) { tu_fifo_read_n(&s->edpt.ff, buffer, (uint16_t)(frames * s->frame_bytes)); - audioh_stream_capture_xfer(s); // re-arm: the FIFO has room again } return frames; } diff --git a/src/class/audio/audio_host.h b/src/class/audio/audio_host.h index 74dfdcdb6..9fcbe2577 100644 --- a/src/class/audio/audio_host.h +++ b/src/class/audio/audio_host.h @@ -45,7 +45,8 @@ extern "C" { // Depth in bytes of the per-stream data FIFO. The FIFO decouples the // application's read/write calls from the endpoint's isochronous polling cadence -// and absorbs rate differences. 1024 bytes hold 4 default (256 B) packets. +// and absorbs rate differences. Capture overwrites the oldest frames when full. +// 1024 bytes hold 4 default (256 B) packets. #ifndef CFG_TUH_AUDIO_STREAM_BUFSIZE #define CFG_TUH_AUDIO_STREAM_BUFSIZE 1024 #endif @@ -240,7 +241,8 @@ void tuh_audio_umount_cb(uint8_t idx); void tuh_audio_capture_cb(uint8_t idx, uint8_t stream_idx, uint16_t xferred_bytes); // Invoked when an isochronous OUT transfer completes successfully: the -// next queued packet is submitted from the stream's playback FIFO. +// next playback packet is submitted from the stream FIFO, or as silence when +// a complete packet is not queued. void tuh_audio_playback_cb(uint8_t idx, uint8_t stream_idx, uint16_t xferred_bytes); // Invoked when an isochronous transfer fails. The stream is stopped |
