Skip to content

libdmaudio and the dmaudio service: mixing streams for the audio devices - #1

Merged
JohnAmadis merged 1 commit into
mainfrom
feature/libdmaudio
Oct 11, 2026
Merged

JohnAmadis merged 1 commit into
mainfrom
feature/libdmaudio

Conversation

@JaxCalder

Copy link
Copy Markdown

Summary

The top layer of the audio stack: a mixer for the audio devices.

  • libdmaudio (library, one in the system) holds everything:
    • Streams. An application opens a stream on a device (by name, or the first one) in the format of its own samples: any rate, 1 to 8 channels, S16/S24/S32. It writes into the stream's buffer (200 ms by default), which is lock-free with one writer and one reader. A write waits while the buffer is full.
    • Mixing. Each stream is converted to the device's format (linear interpolation of the rate, channel mapping), scaled by its volume (square curve) and summed in 24 bits, clamped on output.
    • Serving a device. libdmaudio_serve() configures the node (AUDIO_CONFIGURE; a fixed-format SAI keeps its own) and takes the format from AUDIO_GET_STREAM. It then mixes and writes 10 ms periods, paced by the node's DMA rather than a timer. After 2 s with nothing to play it drains and stops the node; the next samples start it again. The master volume and mute go to the node, or to the mix when the node has none.
    • Streams of processes that end without closing them are dropped.
  • dmaudio (service) is main() calling libdmaudio_serve(), under 1 kB. It runs at priority 3, above the applications. configs/dmaudio.rules starts dmaudio@<name> for every node reported as audio; options go in the unit's args=.
  • Tools: audiotone (sine tones, one stream each), audioplay (PCM WAV), audioctl (list devices, volume, mute).
  • docs/api-reference.md, README.

Not implemented yet: capture (recording streams), and a better resampler than linear interpolation.

Testing

  • test_libdmaudio (host, 16 steps) mixes a device registered without a node. It covers conversions, mono/stereo, S24/S32 output, sums and clamping, volume, 24→48 kHz interpolation, full buffers and timeouts, drain, underruns, pause, and a device that goes away.
  • STM32F746G-DISCO with dmsai 0.1, dmwm8994 0.2 and these modules placed in dmod-boot's build/dmf by hand (not released yet):
    • dmaudio wm8994 /dev/wm8994 started by the rule. This needs Report the nodes after their friends, on the hot-plug worker dmdevfs#69; with dmdevfs 1.5 the codec is not reported.
    • audiotone -f 440 -t 3: 22050 Hz mono into 48 kHz stereo, 3.45 s, 0 underruns.
    • audiotone with three tones at once: three streams mixed, 0 underruns.
    • audioplay of a 3 s 44.1 kHz 24-bit stereo WAV from the SD card: 3.48 s.
    • I checked the timing and the underrun counters only; I did not listen to the output.

🤖 Generated with Claude Code

libdmaudio (one in the system) holds everything: applications open
streams on a device in the format of their samples (any rate, 1-8
channels, S16/S24/S32) and write into lock-free buffers of their own; the
mixer converts each to the device's format (linear interpolation of the
rate, channel mapping), scales it by its volume and sums them in 24 bits.
libdmaudio_serve() serves a device's node: it configures it, mixes a
period at a time and writes it, paced by the node's DMA, stops the node
while nothing plays, and applies the master volume (on the node or the
mix). Streams of processes that are gone are dropped.

dmaudio is the service - main() calling libdmaudio_serve() -, started per
audio device by configs/dmaudio.rules. audiotone, audioplay (WAV) and
audioctl are tools; test_libdmaudio tests the mixer without a node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JohnAmadis
JohnAmadis merged commit a34f3cc into main Oct 11, 2026
1 check 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