Skip to content

usb_audio: allocate the mic scratch buffer from the port heap - #4

Merged
relic-se merged 1 commit into
relic-se:usb-audio-stereofrom
mikeysklar:usb-audio-portmalloc
Aug 13, 2026
Merged

usb_audio: allocate the mic scratch buffer from the port heap#4
relic-se merged 1 commit into
relic-se:usb-audio-stereofrom
mikeysklar:usb-audio-portmalloc

Conversation

@mikeysklar

Copy link
Copy Markdown

Scott asked for port_malloc() on the mic scratch buffer in review. I had all
three platforms on the farm today, so I incorporated the suggestion and tested it
rather than leave it hanging.

board 4 s @ 48 kHz stereo L 1k / 3k R 1k / 3k
Metro RP2040 768044 bytes 17999.4 / 0.3 0.0 / 17999.4
Metro RP2350 768044 bytes 17999.4 / 0.3 0.0 / 17999.4
Feather nRF52840 768044 bytes 17999.4 / 0.3 0.0 / 17999.4

Same numbers as the static buffer, and the nRF52840 keeps both directions on
endpoint 8. On RP2040 .bss drops 188 bytes, the 192 freed less 4 for the
pointer, against 32 bytes of added text.

Two details beyond the straight swap. enable() can be called more than once, so
it frees any chunk sized for a previous rate first, and disable() frees too. I
tested a boot.py calling enable(16000) then enable(48000) and the second one
wins.

Yours to take or leave.

🤖 Generated with Claude Code

Scott suggested port_malloc() for this in review. It replaces the static
int16_t[USB_AUDIO_MAX_SAMPLE_RATE / 1000 * USB_AUDIO_N_CHANNELS] with an
allocation sized by enable() for 1 ms at the negotiated rate, so a board that
never calls enable() pays nothing for it. On Metro RP2040 .bss drops 188 bytes,
the 192 freed less 4 for the pointer, against 32 bytes of added text.

enable() can be called more than once, so it releases any chunk sized for a
previous rate before taking a new one, and disable() frees as well.

Tested 4 s at 48 kHz stereo, 1 kHz left and 3 kHz right, on Metro RP2040, Metro
RP2350 and Feather nRF52840. All three write 768044 bytes with full channel
separation, matching the static buffer exactly. The nRF52840 keeps both
directions on endpoint 8.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@mikeysklar

Copy link
Copy Markdown
Author

That de-allocation bug is the case this covers. port_heap_init() runs once in
main(), not per VM, so the buffer survives the boot.py to code.py teardown that
cleared m_malloc_without_collect(). The tests above all enable() in boot.py
and stream from code.py, which is that exact transition.

@relic-se

Copy link
Copy Markdown
Owner

Two details beyond the straight swap. enable() can be called more than once, so it frees any chunk sized for a previous rate first, and disable() frees too. I tested a boot.py calling enable(16000) then enable(48000) and the second one wins.

Thanks for validating this as well. Niche use-case, but I could see it happening.

@relic-se

Copy link
Copy Markdown
Owner

Looking great. Thank you! 🚀

@relic-se
relic-se merged commit 63a8c91 into relic-se:usb-audio-stereo Aug 13, 2026
223 checks passed
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.

2 participants