Skip to content

[alsa-lib] — package the PCM meter scope fixes (not for merge as-is)#24

Open
Gjuju wants to merge 1 commit into
moode-player:mainfrom
Gjuju:feat/alsa-lib-meter-scope-fixes
Open

[alsa-lib] — package the PCM meter scope fixes (not for merge as-is)#24
Gjuju wants to merge 1 commit into
moode-player:mainfrom
Gjuju:feat/alsa-lib-meter-scope-fixes

Conversation

@Gjuju

@Gjuju Gjuju commented Jul 21, 2026

Copy link
Copy Markdown

Update 2026-07-23 — this PR changes status. It was opened as a test/discussion PR; after yesterday's investigation it is a release-relevant fix. Everything below was re-validated on a freshly flashed Pi 3B, both libraries built on the box, every prediction posted before its run.

What changed

moOde #769 (binding the Peppy softvol control to the real hardware mixer element) made the ALSA chain correct: on a hardware-volume DAC, softvol now removes itself and the chain is transparent. Before #769, that stray softvol silently refused DSD, so MPD always converted upstream and native DSD never actually reached the DAC while Peppy was enabled — the old chain only looked like it played DSD.

Correctness exposed a latent alsa-lib defect: with the chain transparent, native DSD reaches the type meter, whose s16 scope has no DSD conversion — and instead of failing gracefully it asserts and kills the caller:

mpd: pcm_meter.c:1222: snd_pcm_scope_s16_get_channel_buffer: Assertion `s16->buf_areas' failed.
systemd: mpd.service: Main process exited, code=killed, status=6/ABRT

Conditions: Peppy enabled + DSD-capable DAC + hardware volume + dop "no" (the default) + a DSD file. Verified on a virgin Pi: the stock 10.3.0 image (pre-#769) plays the same file fine; after deploying develop, same box, same DAC, same file, MPD aborts. 10.3.1 ships this combination.

What each patch does — measured, one variable at a time

The recipe carries the two patches as separate files, mirroring alsa-project #516/#517 (both reopened upstream):

alsa-lib peppy-alsa dop=no (native DSD) dop=yes (DoP)
stock stock 💥 MPD aborts needles frozen
this PR (#24 = alsa-lib #516 + #517) stock ✅ plays bit-perfect, needles move needles frozen
stock #23 (DoP patch) 💥 MPD aborts ✅ needles move
#24 #23

alsa_lib_scope_no_abort.patch is the release fix — no abort, regardless of format or DSD settings (it also covers S24_3LE/float, which crash the same way). alsa_lib_scope_dsd_levels.patch is the feature on top — real needles on native DSD. Since any decision means rebuilding alsa-lib once, both ship in one build here; if you want a crash-fix-only package, drop the second rbl_patch line and the .deb is minimal.

The honest cost, unchanged

This is still libasound, the library everything links, and the package needs apt-mark hold until the upstream fixes reach a base OS (years, not months). The alternative that also protects 10.3.1 without a held library is a moOde-side guard — refuse or convert when Peppy is enabled with dop "no" on a DSD-capable DAC — at the price of no meter on native DSD. Your call; the DoP column is covered either way by the peppy-alsa patch in #23.

@Gjuju
Gjuju force-pushed the feat/alsa-lib-meter-scope-fixes branch 2 times, most recently from d6a0509 to f20e0eb Compare July 22, 2026 10:12
@moodeaudio

Copy link
Copy Markdown
Contributor

Interesting.

Assuming the S16 fix is in place, won't MPD perform DSD to PCM on-the-fly conversion with Peppy in the audio chain bypassing the proposed DSD level analysis?

@Gjuju

Gjuju commented Jul 22, 2026

Copy link
Copy Markdown
Author

On your question: you're right about this #24, the bit-density decode. It only fires when the DAC takes native DSD and dop=no; once MPD converts to PCM, or the stream is DoP, it never triggers — inert, not wrong. But it is only half of #24, and the smaller half: the other patch, #24, is a crash fix that fires regardless of any DSD setting. That distinction is what I don't think came across, and it starts with the two PRs patching different components:

MPD → type meter ─┬─ s16 scope      (alsa-lib)     ← #24
                  └─ peppyalsa scope (libpeppyalsa) ← #23
                → DAC (untouched)

The s16 scope produces the S16 buffer; libpeppyalsa consumes it. #23 can only act if that buffer exists and holds something — alsa-lib decides both.

#23 — libpeppyalsa
#24 — alsa-lib s16 scope

Worth stating plainly: none of this applies unless Peppy is enabled. The type meter plugin is only in the chain when the VU meter or spectrum is on, and the crash needs a scope reading the s16 buffer. With Peppy off, there is nothing to fix and nothing to break — which bounds how many users are affected, and equally how much risk either patch carries.

Stream Before After #23 (libpeppyalsa) After #24 (alsa-lib)
Native DSD MPD aborts, "Socket open failed" — alsa-lib fails before the plugin sees a sample #23: no crash, playback fine · #24: correct needles
DoP plays, needles frozen at ~4/100 — the top 16 bits are the 0x05/0xFA marker, read as an amplitude correct needles — marker detected, bit density counted — the format converts fine
PCM plays, correct needles — bit-for-bit fallback

What #24 adds to #23: a crash #23 cannot reach — different component, and it kills the process before libpeppyalsa runs — plus the native-DSD case #23 excludes by design. No overlap.

The two are independent: #23 is a leaf package at no system cost, #24 asks moOde to ship and hold its own libasound.

@Gjuju

Gjuju commented Jul 22, 2026

Copy link
Copy Markdown
Author

Sorry, I edited many times , was hard to explain ;-)

@moodeaudio

Copy link
Copy Markdown
Contributor

No prob.

From my memory on DSD and Peppy:
https://moodeaudio.org/forum/showthread.php?tid=3484&page=68

The user @ArthurDent42 reports playing DSD native and MPD converting to PCM w/o experiencing any errors. I think this may be due to his config having either SoX on at 16bit or CamillaDSP enabled. Both of these will allow MPD to do on-the-fly conversion to PCM and avoid the ""Socket open failed" error.

Also, there is a solution that implements an MPD configuration with two parallel audio outputs:

  1. PCM output for peppyalsa
  2. DSD output for the DAC

Something like this in mpd.conf

audio_output {
    type "alsa"
    name "PeppyMeter Output"
    device "peppyalsa"
    format "44100:16:2"
}
audio_output {
    type "alsa"
    name "My DSD DAC"
    device "hw:0,0"
    dsd_native "yes"
    dsd_native_type "2"
}

I recall testing this but deferring any work on it because of the messiness of manipulating the configs and audio chain setup.

@Gjuju
Gjuju marked this pull request as draft July 22, 2026 16:33
@Gjuju

Gjuju commented Jul 22, 2026

Copy link
Copy Markdown
Author

I had a hard investigation day... at first I couldn't reproduce the bug this PR fixes — my test box turned out to be running the patched lib without me knowing (leftover LD_LIBRARY_PATH from an earlier test session). So I wiped everything and validated on a freshly flashed Pi. Now I know exactly why I had to solve this bug, and it matters for 10.3.1.

The crash is exposed by #769 (Peppy: bind the softvol control to the hardware mixer element — my own PR).

Before #769, the softvol control name never matched a hardware element, so softvol stayed active in the Peppy chain. Softvol only accepts linear PCM (S16/S24/S32) — it refuses DSD at negotiation — so MPD always converted DSD to PCM upstream and the meter only ever saw PCM. That softvol was acting, by accident, as a shield.

After #769, alsa-lib recognises the hardware control and softvol removes itself from the chain ("hardware control - no need for softvol" in pcm_softvol.c). The chain becomes transparent — which is what we wanted for the double attenuation — but now, with a DSD-capable DAC + hardware volume + dop=no, native DSD reaches the type meter, whose s16 scope has no DSD conversion, and instead of failing gracefully it asserts and kills MPD:

mpd: pcm_meter.c:1222: snd_pcm_scope_s16_get_channel_buffer: Assertion `s16->buf_areas' failed.
systemd: mpd.service: Main process exited, code=killed, status=6/ABRT

The UI shows "Socket open failed", and with a DSD album queued it's a crash loop.

Reproduced end-to-end on a freshly flashed Pi 3B, everything stock, same box, same DAC, same .dsf, minutes apart:

  • stock 10.3.0 image (pre-#769): plays fine, needles move (MPD converts, softvol shields)
  • after deploying develop (with #769): MPD aborts at pcm_meter.c:1222

Conditions: Peppy enabled + DSD-capable DAC + hardware volume + dop=no (the default) + a DSD file. 10.3.1 will ship this combination.

The alsa-lib patch in this PR fixes the crash (silent buffer instead of abort) and the second patch gives real needles on native DSD as a bonus. The alternative, if holding a patched libasound is too heavy, is a moOde-side guard: make Peppy + dop=no on a DSD-capable DAC impossible (UI notice, or force conversion when the tap is active). Your call — but 10.3.1 should probably not ship without one of the two.

@Gjuju
Gjuju marked this pull request as ready for review July 22, 2026 22:13
@Gjuju Gjuju changed the title [alsa-lib] TEST — package the PCM meter scope fixes (not for merge as-is) [alsa-lib] — package the PCM meter scope fixes (not for merge as-is) Jul 22, 2026
@moodeaudio

Copy link
Copy Markdown
Contributor

Got it. That explains the MPD error now w/native DSD+peppy.

I don't think its a big enough issue to hold back shipping 10.3.1 though because I really need to get our users those RB bug fixes :-) I can mention the DSD thing in the release announcement.

I'm not opposed to deploying these patches after testing but I'm a little unclear.

  • Does the alsa-lib patch allow MPD to do on-the-fly PCM conversion when peppy is in the chain?

@Gjuju

Gjuju commented Jul 22, 2026

Copy link
Copy Markdown
Author

Good timing for that question, because the answer is "no — it's the opposite", and it splits into the two options on the table.

The alsa-lib patch does NOT make MPD convert. With it, native DSD flows bit-perfect through the whole chain to the DAC — nothing converts anywhere. What changes is inside the meter: the s16 scope stops aborting when it meets a format it cannot convert (that's the crash fix), and it reads the level directly from the DSD bit density (that's the second patch — the needles move on native DSD itself). MPD's own on-the-fly conversion only ever triggers when something in the chain refuses DSD — a PCM-only DAC, a DSP. The alsa-lib patch removes the last reason for that refusal, it doesn't add one.

The thing that makes MPD convert when Peppy is in the chain is a different, moode-side fix — and since you're shipping 10.3.1 without holding it, it's exactly what it was designed for. One file, peppy.conf: a unity route above the meter that only negotiates linear PCM. Native DSD gets refused there, MPD falls back to its own dsd2pcm, and the meter reads the converted PCM — which is precisely what happened by accident before my softvol fix (the stray softvol refused DSD the same way). Unity gain, no control created, so no double attenuation; PCM and DoP pass bit-identical (verified: the DoP markers survive below the route). Nothing for users to enable. I'm opening it as a separate PR.

So the two compose:

native DSD + Peppy needles on native DSD cost
peppy.conf guard (moode PR) plays — MPD converts yes (via converted PCM) one conf file, revert when alsa-lib is fixed
alsa-lib patches (this PR) plays bit-perfect, no conversion yes (bit-density decode) rebuilt libasound + apt-mark hold

Both were validated yesterday on a freshly flashed Pi 3B, each variable isolated. The guard can ship in 10.3.1 or right after with zero risk to anything else in the chain; this PR is the full fix for whenever you're ready to test it, and the guard's file comment already says to remove it then.

See moode-player/moode#779

@moodeaudio

Copy link
Copy Markdown
Contributor

Still confused.

Currently with 10.3.1 (+769 patch), Hardware volume, Peppy on and a native DSD DAC, playing a DSD file throws the MPD error.

With just the alsa-lib patch what happens?

@Gjuju

Gjuju commented Jul 22, 2026

Copy link
Copy Markdown
Author

It plays — bit-perfect native DSD, and the needles move: the first patch stops the abort, the second reads the level from the DSD bit density.

Measured yesterday on a freshly flashed Pi 3B (stock everything except this PR's libasound), Hardware volume, Peppy on, dop "no", a DSD64 file: card gets DSD_U32_BE @ 88200, FIFO levels vary with the music, MPD NRestarts=0, no assertion.

So: same DSD reaching the DAC as today, minus the crash, plus working needles — nothing else in the chain changes.

Before #769 the stray softvol refused native DSD so MPD silently converted to PCM (needles moved, but the DAC never got native DSD); with this patch the chain stays transparent — native DSD reaches the DAC bit-perfect, the meter no longer aborts, and the needles move from the DSD bit density itself.

@moodeaudio

Copy link
Copy Markdown
Contributor

I don't understand this regarding just the alsa-lib patch It plays — bit-perfect native DSD, and the needles move

If MPD is playing native DSD then how can PCM peppy needles move?

@Gjuju

Gjuju commented Jul 23, 2026

Copy link
Copy Markdown
Author

That's exactly what the alsa_lib_scope_dsd_levels.patch does: it doesn't convert DSD to PCM, it counts the density of 1-bits over a short window — in a DSD stream that density is the amplitude. So it derives a level directly from the native DSD, no PCM needed. The DAC still gets untouched native DSD.

peppyalsa never sees the raw stream — it only reads the S16 buffer alsa-lib's scope produces, and on native DSD that scope had no conversion, so it asserted before peppyalsa ever ran (that's the crash). alsa_lib_scope_no_abort.patch stops the assert (empty buffer), and alsa_lib_scope_dsd_levels.patch fills that buffer with a level derived from the DSD bit density — so peppyalsa's needles move without peppyalsa itself being touched.

moOde uses the distro's alsa-lib, whose `type meter` scope - the one
libpeppyalsa taps for the VU meter - has two defects that show up on DSD.

The first is a crash, and it is not DSD-specific. s16_enable() returns
-EINVAL for any format it cannot convert to S16: the DSD formats, the
3-byte packed ones (S24_3LE and friends, which plenty of USB DACs expose
natively), float. The failure is swallowed, the buffer is never allocated,
and the accessor that scopes call then asserts on it - so MPD dies
mid-playback with "Socket open failed". alsa-lib does this to its own
level scope too. The patch hands out a silent buffer instead.

The second is the missing feature behind it: with the crash gone, needles
still sit still on DSD because the scope has no conversion for it. DSD
carries no sample values, only a bit density - over a short window the
proportion of ones is the amplitude - so one S16 sample per frame can be
recovered by counting bits. The scale follows the format: 0 dBFS in DSD is
a 50% modulation index.

Verified on the same master in both formats: native DSD64 reads min 2 /
max 21 / mean 7.1 where its FLAC 96/24 reads min 2 / max 22 / mean 7.6.
The DAC keeps receiving DSD_U32_BE bit-perfect - the meter is a passive
tap and neither patch touches what is played.

Both are submitted upstream as alsa-project/alsa-lib #516 and #517. Drop
this package once they are released and reach the distro.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Gjuju
Gjuju force-pushed the feat/alsa-lib-meter-scope-fixes branch from f20e0eb to d2b65cc Compare July 23, 2026 08:11
@moodeaudio

Copy link
Copy Markdown
Contributor

Ok, makes sense now.

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