Skip to content

midi device: add cable-aware stream read (tud_midi_n_demux_stream_read)#3514

Merged
HiFiPhile merged 1 commit intohathach:masterfrom
sauloverissimo:fix/midi-device-cable-demux
Mar 1, 2026
Merged

midi device: add cable-aware stream read (tud_midi_n_demux_stream_read)#3514
HiFiPhile merged 1 commit intohathach:masterfrom
sauloverissimo:fix/midi-device-cable-demux

Conversation

@sauloverissimo
Copy link
Contributor

Summary

Implements the tud_midi_n_demux_stream_read() function proposed in #1838 — a cable-aware variant of tud_midi_n_stream_read() that returns the cable number of the data actually read.

The existing tud_midi_n_stream_read() accepts a cable_num parameter but ignores it ((void) cable_num), silently mixing data from all virtual cables into a single stream. This makes it impossible to build multi-port MIDI devices where each cable maps to a separate DIN jack or logical MIDI port.

New API

// Multiple interfaces (CFG_TUD_MIDI > 1)
uint32_t tud_midi_n_demux_stream_read(uint8_t itf, uint8_t *p_cable_num,
                                       void *buffer, uint32_t bufsize);

// Single interface convenience wrapper
uint32_t tud_midi_demux_stream_read(uint8_t *p_cable_num,
                                     void *buffer, uint32_t bufsize);

Reads MIDI byte-stream data from the RX FIFO. Peeks at each USB-MIDI event packet header before consuming it and stops when the next packet belongs to a different cable, allowing callers to dispatch per-cable without losing data. Sets *p_cable_num to the cable number of the returned data.

Usage example

uint8_t cable_num;
uint8_t buf[64];
uint32_t n;

while ((n = tud_midi_demux_stream_read(&cable_num, buf, sizeof(buf))) > 0) {
    // route buf[0..n-1] to the handler for cable_num
    midi_port_feed(cable_num, buf, n);
}

Implementation details

  • Follows the API shape proposed by @rppicomidi in [Midi] Cable_num working for available and read #1838
  • Mirrors the approach already used by the host-side tuh_midi_stream_read(): uses tu_edpt_stream_peek() for cable inspection before consuming packets
  • CIN-based byte count from USB MIDI 1.0 Table 4-1
  • Leftover handling via the existing midi_driver_stream_t
  • No changes to existing functions — fully additive, backward compatible

Files changed

  • src/class/midi/midi_device.c — new tud_midi_n_demux_stream_read() function
  • src/class/midi/midi_device.h — declaration + single-interface wrapper

Closes #1838

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a cable-aware stream read function tud_midi_n_demux_stream_read() for USB-MIDI devices, addressing issue #1838. The existing tud_midi_n_stream_read() function ignores its cable_num parameter and mixes data from all virtual cables, making it impossible to build multi-port MIDI devices where each cable maps to a separate physical MIDI port.

Changes:

  • Added tud_midi_n_demux_stream_read() that returns the cable number of data actually read
  • Added single-interface convenience wrapper tud_midi_demux_stream_read()
  • Function peeks at USB-MIDI packet headers to stop when the next packet belongs to a different cable

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/class/midi/midi_device.h Added function declarations and inline wrapper for cable-demultiplexing stream read
src/class/midi/midi_device.c Implemented cable-aware stream read using peek-before-consume pattern similar to host-side implementation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

The existing tud_midi_n_stream_read() accepts a cable_num parameter but
ignores it — all cables share a single FIFO and stream parser state, so
data from different virtual cables is silently mixed together.

Add tud_midi_n_demux_stream_read() which returns the cable number of the
data that was actually read.  It peeks at each USB-MIDI event packet
header before consuming it and stops when the next packet belongs to a
different cable, allowing callers to dispatch per-cable without losing
data.

Implementation details:
- Mirrors the host-side tuh_midi_stream_read() approach: tu_edpt_stream_peek
  for cable inspection, CIN-based byte count (USB MIDI 1.0 Table 4-1),
  leftover handling via existing midi_driver_stream_t
- *p_cable_num initialized to 0xff sentinel so callers can detect
  "no data" even when return value is 0
- Cable-change check (total_read > 0 guard) covers both leftover-originated
  reads and freshly consumed packets
- TU_VERIFY uses explicit != NULL comparisons, consistent with codebase style
- Note: shares stream->buffer with tud_midi_n_stream_read(); do not mix
  calls on the same interface
- Adds single-interface convenience wrapper tud_midi_demux_stream_read()

Closes hathach#1838
@sauloverissimo sauloverissimo force-pushed the fix/midi-device-cable-demux branch from 9a08c74 to 4d40219 Compare February 25, 2026 15:39
@github-actions
Copy link

Size Difference Report

Because TinyUSB code size varies by port and configuration, the metrics below represent the averaged totals across all example builds.

Note: If there is no change, only one value is shown.

Changes >1% in size

No entries.

Changes <1% in size

No entries.

No changes
file .text .rodata .data .bss size % diff
audio_device.c 2849 0 1248 1673 4518 +0.0%
cdc_device.c 1328 16 19 661 1988 +0.0%
cdc_host.c 6610 487 15 1539 8371 +0.0%
dcd_ch32_usbfs.c 1472 0 0 2444 3916 +0.0%
dcd_ch32_usbhs.c 1648 0 0 448 2096 +0.0%
dcd_ci_fs.c 1925 0 0 1290 3215 +0.0%
dcd_ci_hs.c 1762 0 0 1280 2530 +0.0%
dcd_da146xx.c 3067 0 0 144 3211 +0.0%
dcd_dwc2.c 4174 25 0 265 4463 +0.0%
dcd_eptri.c 2270 0 0 259 2529 +0.0%
dcd_khci.c 1953 0 0 1290 3243 +0.0%
dcd_lpc17_40.c 1470 0 0 648 1794 +0.0%
dcd_lpc_ip3511.c 1463 0 0 264 1639 +0.0%
dcd_mm32f327x_otg.c 1478 0 0 1290 2768 +0.0%
dcd_msp430x5xx.c 1796 0 0 176 1972 +0.0%
dcd_musb.c 2446 0 0 160 2606 +0.0%
dcd_nrf5x.c 2919 0 0 292 3211 +0.0%
dcd_nuc120.c 1093 0 0 78 1171 +0.0%
dcd_nuc121.c 1167 0 0 101 1268 +0.0%
dcd_nuc505.c 0 0 1529 157 1686 +0.0%
dcd_rp2040.c 859 20 604 655 2138 +0.0%
dcd_rusb2.c 2917 0 0 156 3073 +0.0%
dcd_samd.c 1032 0 0 266 1298 +0.0%
dcd_samg.c 1319 0 0 72 1391 +0.0%
dcd_stm32_fsdev.c 2557 0 0 291 2848 +0.0%
dfu_device.c 744 28 712 183 926 +0.0%
dfu_rt_device.c 156 0 134 0 156 +0.0%
dwc2_common.c 601 30 0 0 618 +0.0%
ecm_rndis_device.c 1037 0 1 2272 3310 +0.0%
ehci.c 2761 0 0 5970 7537 +0.0%
fsdev_common.c 180 0 0 0 180 +0.0%
hcd_ch32_usbfs.c 2484 0 0 498 2982 +0.0%
hcd_ci_hs.c 190 0 0 0 190 +0.0%
hcd_dwc2.c 4970 32 1 512 5516 +0.0%
hcd_khci.c 2442 0 0 449 2891 +0.0%
hcd_musb.c 3073 0 0 157 3230 +0.0%
hcd_pio_usb.c 262 0 240 0 502 +0.0%
hcd_rp2040.c 976 73 416 384 1849 +0.0%
hcd_rusb2.c 2923 0 0 245 3168 +0.0%
hcd_samd.c 2220 0 0 324 2544 +0.0%
hcd_stm32_fsdev.c 3282 0 1 420 3703 +0.0%
hid_device.c 1118 44 997 115 1233 +0.0%
hid_host.c 1206 0 0 1250 2456 +0.0%
hub.c 1235 8 8 29 1268 +0.0%
midi_device.c 1127 0 991 589 1714 +0.0%
midi_host.c 1353 7 7 3740 5097 +0.0%
msc_device.c 2518 108 2286 538 3056 +0.0%
msc_host.c 1589 0 0 394 1984 +0.0%
mtp_device.c 1689 22 1449 579 2275 +0.0%
ncm_device.c 1514 28 1408 5830 7358 +0.0%
ohci.c 1942 0 0 2414 4356 +0.0%
rp2040_usb.c 172 75 718 4 969 +0.0%
rusb2_common.c 160 0 16 0 176 +0.0%
tusb.c 429 0 368 3 430 +0.0%
tusb_fifo.c 843 0 477 0 838 +0.0%
typec_stm32.c 820 8 2 12 842 +0.0%
usbc.c 420 2 20 166 608 +0.0%
usbd.c 3191 57 89 276 3531 +0.0%
usbd_control.c 523 0 474 78 600 +0.0%
usbh.c 4395 60 63 954 5436 +0.0%
usbtmc_device.c 2176 24 69 291 2500 +0.0%
vendor_device.c 624 0 530 464 1087 +0.0%
video_device.c 4391 5 1851 472 4855 +0.0%
TOTAL 113310 1159 16743 45511 160914 +0.0%

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Collaborator

@HiFiPhile HiFiPhile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you !

@HiFiPhile HiFiPhile merged commit bf76bb8 into hathach:master Mar 1, 2026
287 checks passed
@sauloverissimo
Copy link
Contributor Author

Thanks @HiFiPhile for the merge! Deleting the branch on my end.

@sauloverissimo sauloverissimo deleted the fix/midi-device-cable-demux branch March 1, 2026 15:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Midi] Cable_num working for available and read

3 participants