From f08f7036fba25f20f8b4f02f38e4a92a9aa1cf69 Mon Sep 17 00:00:00 2001 From: Julien Gainza Date: Wed, 22 Jul 2026 01:08:15 +0200 Subject: [PATCH 1/3] [alsa-lib] Package the PCM meter scope fixes 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 --- .../alsa-lib/alsa_lib_scope_dsd_levels.patch | 165 ++++++++++++++++++ .../alsa-lib/alsa_lib_scope_no_abort.patch | 58 ++++++ packages/alsa-lib/build.sh | 47 +++++ 3 files changed, 270 insertions(+) create mode 100644 packages/alsa-lib/alsa_lib_scope_dsd_levels.patch create mode 100644 packages/alsa-lib/alsa_lib_scope_no_abort.patch create mode 100755 packages/alsa-lib/build.sh diff --git a/packages/alsa-lib/alsa_lib_scope_dsd_levels.patch b/packages/alsa-lib/alsa_lib_scope_dsd_levels.patch new file mode 100644 index 0000000..845efd6 --- /dev/null +++ b/packages/alsa-lib/alsa_lib_scope_dsd_levels.patch @@ -0,0 +1,165 @@ +diff --git a/src/pcm/pcm_meter.c b/src/pcm/pcm_meter.c +index dd57f5ca..4cd1e60d 100644 +--- a/src/pcm/pcm_meter.c ++++ b/src/pcm/pcm_meter.c +@@ -1004,8 +1004,100 @@ typedef struct _snd_pcm_scope_s16 { + int16_t *buf; + snd_pcm_channel_area_t *buf_areas; + int silent; ++ unsigned int dsd_bits; /* DSD bits per frame and channel */ ++ unsigned int dsd_shift; /* smoothing time constant, in frames */ ++ int32_t *dsd_states; + } snd_pcm_scope_s16_t; + ++/* DSD carries no sample values, only a bit density: over a short window the ++ * proportion of ones IS the amplitude, one half being silence. So one S16 sample ++ * per frame can be recovered by counting the bits of that frame and smoothing the ++ * result, which is what the scopes need - a level, not a reconstruction. ++ * ++ * Counting bits is invariant to their order, so the LE and BE variants share one ++ * code path and only the width of a frame matters. ++ * ++ * Two things set the quality. The count itself is noisy - 32 bits give a standard ++ * deviation of 0.5/sqrt(32) - hence the smoothing, whose time constant is taken ++ * from the rate so that it is a duration rather than a number of frames, and so ++ * behaves the same from DSD64 to DSD512. And the scale: 0 dBFS in DSD is a 50% ++ * modulation index, so a full-scale signal only swings the density between 25% and ++ * 75%; referring to the whole range would read 6 dB low. ++ */ ++static unsigned int dsd_frame_bits(snd_pcm_format_t format) ++{ ++ switch (format) { ++ case SND_PCM_FORMAT_DSD_U8: ++ return 8; ++ case SND_PCM_FORMAT_DSD_U16_LE: ++ case SND_PCM_FORMAT_DSD_U16_BE: ++ return 16; ++ case SND_PCM_FORMAT_DSD_U32_LE: ++ case SND_PCM_FORMAT_DSD_U32_BE: ++ return 32; ++ default: ++ return 0; ++ } ++} ++ ++static void s16_dsd_decode(const snd_pcm_channel_area_t *dst_areas, ++ snd_pcm_uframes_t dst_offset, ++ const snd_pcm_channel_area_t *src_areas, ++ snd_pcm_uframes_t src_offset, ++ unsigned int channels, snd_pcm_uframes_t frames, ++ unsigned int bits, unsigned int shift, ++ int32_t *states) ++{ ++ static const unsigned char popcount[256] = { ++#define P2(n) n, n + 1, n + 1, n + 2 ++#define P4(n) P2(n), P2(n + 1), P2(n + 1), P2(n + 2) ++#define P6(n) P4(n), P4(n + 1), P4(n + 1), P4(n + 2) ++ P6(0), P6(1), P6(1), P6(2) ++#undef P6 ++#undef P4 ++#undef P2 ++ }; ++ unsigned int channel, bytes = bits / 8; ++ ++ for (channel = 0; channel < channels; ++channel, ++states) { ++ const snd_pcm_channel_area_t *src_area = &src_areas[channel]; ++ const snd_pcm_channel_area_t *dst_area = &dst_areas[channel]; ++ const unsigned char *src; ++ char *dst; ++ int src_step, dst_step; ++ snd_pcm_uframes_t frames1 = frames; ++ int32_t acc = *states; ++ ++ src = snd_pcm_channel_area_addr(src_area, src_offset); ++ src_step = snd_pcm_channel_area_step(src_area); ++ dst = snd_pcm_channel_area_addr(dst_area, dst_offset); ++ dst_step = snd_pcm_channel_area_step(dst_area); ++ ++ while (frames1-- > 0) { ++ unsigned int i, ones = 0; ++ int sample; ++ ++ for (i = 0; i < bytes; i++) ++ ones += popcount[src[i]]; ++ /* density deviation, doubled because full scale is a ++ * 50% modulation index, in Q15 */ ++ sample = ((int)(2 * ones) - (int)bits) * (2 * 32767) / ++ (int)bits; ++ /* exponential average: acc holds the sum, not the mean */ ++ acc += sample - (acc >> shift); ++ sample = acc >> shift; ++ if (sample > 32767) ++ sample = 32767; ++ else if (sample < -32768) ++ sample = -32768; ++ *(int16_t *)dst = sample; ++ dst += dst_step; ++ src += src_step; ++ } ++ *states = acc; ++ } ++} ++ + static int s16_enable(snd_pcm_scope_t *scope) + { + snd_pcm_scope_s16_t *s16 = scope->private_data; +@@ -1041,6 +1133,20 @@ static int s16_enable(snd_pcm_scope_t *scope) + case SND_PCM_FORMAT_U32_BE: + idx = snd_pcm_linear_convert_index(spcm->format, SND_PCM_FORMAT_S16); + break; ++ case SND_PCM_FORMAT_DSD_U8: ++ case SND_PCM_FORMAT_DSD_U16_LE: ++ case SND_PCM_FORMAT_DSD_U16_BE: ++ case SND_PCM_FORMAT_DSD_U32_LE: ++ case SND_PCM_FORMAT_DSD_U32_BE: ++ s16->dsd_bits = dsd_frame_bits(spcm->format); ++ /* Smooth over about a quarter of a millisecond, taken from the ++ * rate so the behaviour is the same at every DSD rate. */ ++ s16->dsd_shift = 1; ++ while ((1U << (s16->dsd_shift + 1)) <= spcm->rate / 4000 && ++ s16->dsd_shift < 8) ++ s16->dsd_shift++; ++ idx = 0; ++ break; + default: + /* No S16 conversion exists for this format - DSD, the 3-byte + * packed formats, float. Leaving the scope disabled is worse +@@ -1063,6 +1169,11 @@ static int s16_enable(snd_pcm_scope_t *scope) + if (!s16->adpcm_states) + return -ENOMEM; + } ++ if (s16->dsd_bits) { ++ s16->dsd_states = calloc(spcm->channels, sizeof(*s16->dsd_states)); ++ if (!s16->dsd_states) ++ return -ENOMEM; ++ } + /* calloc, not malloc: when s16->silent is set nothing ever writes to + * this buffer and scopes must read silence, not uninitialised memory. + */ +@@ -1091,6 +1202,8 @@ static void s16_disable(snd_pcm_scope_t *scope) + snd_pcm_scope_s16_t *s16 = scope->private_data; + free(s16->adpcm_states); + s16->adpcm_states = NULL; ++ free(s16->dsd_states); ++ s16->dsd_states = NULL; + free(s16->buf); + s16->buf = NULL; + free(s16->buf_areas); +@@ -1154,6 +1267,17 @@ static void s16_update(snd_pcm_scope_t *scope) + s16->index, + s16->adpcm_states); + break; ++ case SND_PCM_FORMAT_DSD_U8: ++ case SND_PCM_FORMAT_DSD_U16_LE: ++ case SND_PCM_FORMAT_DSD_U16_BE: ++ case SND_PCM_FORMAT_DSD_U32_LE: ++ case SND_PCM_FORMAT_DSD_U32_BE: ++ s16_dsd_decode(s16->buf_areas, offset, ++ meter->buf_areas, offset, ++ spcm->channels, frames, ++ s16->dsd_bits, s16->dsd_shift, ++ s16->dsd_states); ++ break; + default: + snd_pcm_linear_convert(s16->buf_areas, offset, + meter->buf_areas, offset, diff --git a/packages/alsa-lib/alsa_lib_scope_no_abort.patch b/packages/alsa-lib/alsa_lib_scope_no_abort.patch new file mode 100644 index 0000000..508545b --- /dev/null +++ b/packages/alsa-lib/alsa_lib_scope_no_abort.patch @@ -0,0 +1,58 @@ +diff --git a/src/pcm/pcm_meter.c b/src/pcm/pcm_meter.c +index 68c369de..dd57f5ca 100644 +--- a/src/pcm/pcm_meter.c ++++ b/src/pcm/pcm_meter.c +@@ -1003,6 +1003,7 @@ typedef struct _snd_pcm_scope_s16 { + snd_pcm_uframes_t old; + int16_t *buf; + snd_pcm_channel_area_t *buf_areas; ++ int silent; + } snd_pcm_scope_s16_t; + + static int s16_enable(snd_pcm_scope_t *scope) +@@ -1041,7 +1042,20 @@ static int s16_enable(snd_pcm_scope_t *scope) + idx = snd_pcm_linear_convert_index(spcm->format, SND_PCM_FORMAT_S16); + break; + default: +- return -EINVAL; ++ /* No S16 conversion exists for this format - DSD, the 3-byte ++ * packed formats, float. Leaving the scope disabled is worse ++ * than useless: scopes that depend on it, including our own ++ * level scope, call snd_pcm_scope_s16_get_channel_buffer() ++ * from their update callback and that assert()s on the buffer ++ * this function never allocated, killing the application. ++ * Hand out a silent buffer instead, so a scope reads zero on a ++ * format it cannot see rather than aborting the process. ++ */ ++ SNDERR("s16 scope: no S16 conversion for format %s, scopes will read silence", ++ snd_pcm_format_name(spcm->format)); ++ s16->silent = 1; ++ idx = 0; ++ break; + } + s16->index = idx; + if (spcm->format == SND_PCM_FORMAT_IMA_ADPCM) { +@@ -1049,7 +1063,10 @@ static int s16_enable(snd_pcm_scope_t *scope) + if (!s16->adpcm_states) + return -ENOMEM; + } +- s16->buf = malloc(meter->buf_size * 2 * spcm->channels); ++ /* calloc, not malloc: when s16->silent is set nothing ever writes to ++ * this buffer and scopes must read silence, not uninitialised memory. ++ */ ++ s16->buf = calloc(meter->buf_size * 2 * spcm->channels, 1); + if (!s16->buf) { + free(s16->adpcm_states); + return -ENOMEM; +@@ -1101,6 +1118,11 @@ static void s16_update(snd_pcm_scope_t *scope) + snd_pcm_t *spcm = meter->gen.slave; + snd_pcm_sframes_t size; + snd_pcm_uframes_t offset; ++ if (s16->silent) { ++ /* Nothing to convert; the buffer stays zero. */ ++ s16->old = meter->now; ++ return; ++ } + size = meter->now - s16->old; + if (size < 0) + size += spcm->boundary; diff --git a/packages/alsa-lib/build.sh b/packages/alsa-lib/build.sh new file mode 100755 index 0000000..ce3dad0 --- /dev/null +++ b/packages/alsa-lib/build.sh @@ -0,0 +1,47 @@ +#!/bin/bash +######################################################################### +# +# Build recipe for alsa-lib debian package with the PCM meter scope fixes +# +# The stock library is used as shipped by the distro. These two patches +# only touch the s16 scope of the `type meter` plugin, which is what +# libpeppyalsa taps for the VU meter: +# +# - scope_no_abort: the scope aborts the calling application on any format +# it cannot convert to S16 (DSD, the 3-byte packed formats, float). MPD +# dies mid-playback on a DSD track. It now reads silence instead. +# - scope_dsd_levels: recover a level from a DSD stream by bit density, so +# the needles work on native DSD instead of sitting still. +# +# Both are submitted upstream (alsa-project/alsa-lib #516 and #517), but merging +# there is only the first of four steps: alsa-project must cut a release, Debian +# package it, and the base OS pick it up. Trixie is stable and keeps alsa-lib +# 1.2.14 for its whole life, so the fix reaches users on the NEXT base OS - years, +# not months. Drop this package when a base OS actually ships a fixed alsa-lib. +# +# (C) bitkeeper 2026 http://moodeaudio.org +# License: GPLv3 +# +######################################################################### +. ../../scripts/rebuilder.lib.sh + +PKG_DSC_URL="http://deb.debian.org/debian/pool/main/a/alsa-lib/alsa-lib_1.2.14-1.dsc" + +rbl_prepare_from_dsc_url $PKG_DSC_URL + +#------------------------------------------------------------ +# Custom part of the packing + +# patch and add patches to debian +rbl_patch $BASE_DIR/alsa_lib_scope_no_abort.patch +EDITOR=/bin/true dpkg-source --commit . alsa_lib_scope_no_abort.patch +rbl_patch $BASE_DIR/alsa_lib_scope_dsd_levels.patch +EDITOR=/bin/true dpkg-source --commit . alsa_lib_scope_dsd_levels.patch + +# set debian local suffix flag +DEBFULLNAME=$DEBFULLNAME DEBEMAIL=$DEBEMAIL dch --local $DEBSUFFIX "Added PCM meter scope patches: no abort on unconvertible formats, DSD levels" + +#------------------------------------------------------------ + +rbl_build +echo "done" From 8a45e0b1254a4f5b7b50017d30697022484617da Mon Sep 17 00:00:00 2001 From: Julien Gainza Date: Fri, 24 Jul 2026 10:12:43 +0200 Subject: [PATCH 2/3] [alsa-lib] Follow the distro version instead of pinning 1.2.14 The recipe pinned alsa-lib_1.2.14-1.dsc. That URL disappears from the pool as soon as no suite references it - trixie-proposed-updates already carries 1.2.14-1+deb13u1 - and the pin says nothing about when the package stops being needed, so a base OS moving past 1.2.14 leaves a held library behind with no exit condition. Build the distro's own candidate version instead, and let the source say whether the package is still needed: - ALSA_VERSION defaults to the apt candidate of libasound2t64/libasound2, and the .dsc URL is derived from it. ALSA_MIRROR/ALSA_VERSION override it for a distro carrying its own revision. - Below PATCH_FLOOR the build fails rather than guessing. - Once the unpacked source no longer bails out of s16_enable() on a format it cannot convert, the recipe reports the package obsolete and exits 0. Reading the code rather than comparing version numbers survives a fix that lands re-authored, renumbered, or straight from alsa-devel - of the 193 commits alsa-lib took since january 2025, 165 are the maintainer's own and only 22 reference a pull request at all. - One patch set applies in between. pcm_meter.c is unchanged from 1.2.15 through 1.2.16.1, and the 1.2.14 -> 1.2.15 churn (whitespace, SNDERR replaced by the new log macros) misses the patched hunks; both patches were dry-run against every tag in that range plus master. - rbl_patch tolerates offset and fuzz, so the patched source is checked for the markers each patch introduces before the package is built. Co-Authored-By: Claude Opus 4.8 --- packages/alsa-lib/build.sh | 71 ++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/packages/alsa-lib/build.sh b/packages/alsa-lib/build.sh index ce3dad0..2daa5db 100755 --- a/packages/alsa-lib/build.sh +++ b/packages/alsa-lib/build.sh @@ -17,7 +17,12 @@ # there is only the first of four steps: alsa-project must cut a release, Debian # package it, and the base OS pick it up. Trixie is stable and keeps alsa-lib # 1.2.14 for its whole life, so the fix reaches users on the NEXT base OS - years, -# not months. Drop this package when a base OS actually ships a fixed alsa-lib. +# not months. +# +# The recipe therefore follows the base OS instead of pinning one version: it +# builds whatever alsa-lib the build host's distro offers, refuses versions it +# has not been validated against, and asks the source itself whether it is still +# needed, so nothing here has to track an upstream release or pull request number. # # (C) bitkeeper 2026 http://moodeaudio.org # License: GPLv3 @@ -25,18 +30,72 @@ ######################################################################### . ../../scripts/rebuilder.lib.sh -PKG_DSC_URL="http://deb.debian.org/debian/pool/main/a/alsa-lib/alsa-lib_1.2.14-1.dsc" +# Version to rebuild. Defaults to the distro candidate, so a point release that +# drops the old .dsc from the pool does not break the build. Override both when +# the base OS carries its own alsa-lib revision. +ALSA_MIRROR=${ALSA_MIRROR:-http://deb.debian.org/debian} +ALSA_VERSION=${ALSA_VERSION:-`LC_ALL=C apt-cache policy libasound2t64 libasound2 2>/dev/null | awk '/Candidate:/ && $2 != "(none)" {print $2; exit}'`} + +if [ -z "$ALSA_VERSION" ] +then + echo "${RED}Error: could not determine the alsa-lib version to build, set ALSA_VERSION${NORMAL}" + exit 1 +fi + +ALSA_UPSTREAM=${ALSA_VERSION%-*} + +# Oldest upstream release the patches were validated against +PATCH_FLOOR="1.2.14" + +if dpkg --compare-versions "$ALSA_UPSTREAM" lt "$PATCH_FLOOR" +then + echo "${RED}Error: alsa-lib $ALSA_UPSTREAM is older than $PATCH_FLOOR, the patches are untested there${NORMAL}" + exit 1 +fi + +# One patch set covers 1.2.14 up to 1.2.16.1: pcm_meter.c is unchanged from +# 1.2.15 onwards, and the 1.2.14 -> 1.2.15 churn (whitespace, SNDERR replaced by +# the new log macros) misses the patched hunks. Branch here if a later version +# moves them. +PATCHES="alsa_lib_scope_no_abort.patch alsa_lib_scope_dsd_levels.patch" + +PKG_DSC_URL="$ALSA_MIRROR/pool/main/a/alsa-lib/alsa-lib_${ALSA_VERSION}.dsc" + +echo "building alsa-lib $ALSA_VERSION from $PKG_DSC_URL" rbl_prepare_from_dsc_url $PKG_DSC_URL #------------------------------------------------------------ # Custom part of the packing +# This package only exists while the scope still bails out on a format it cannot +# convert. Ask the source, not a version number: any accepted form of the fix +# drops that bailout, whatever release or pull request it arrives by, and +# upstream numbers are not a reliable key - alsa-project reviews on alsa-devel +# and its maintainer commits most patches himself. +if ! awk '/^static int s16_enable/,/^}/' src/pcm/pcm_meter.c | grep -A2 "^ default:" | grep -q -- "return -EINVAL" +then + echo "alsa-lib $ALSA_UPSTREAM already handles unconvertible formats, this package is obsolete - nothing to build" + exit 0 +fi + # patch and add patches to debian -rbl_patch $BASE_DIR/alsa_lib_scope_no_abort.patch -EDITOR=/bin/true dpkg-source --commit . alsa_lib_scope_no_abort.patch -rbl_patch $BASE_DIR/alsa_lib_scope_dsd_levels.patch -EDITOR=/bin/true dpkg-source --commit . alsa_lib_scope_dsd_levels.patch +for patch in $PATCHES +do + rbl_patch $BASE_DIR/$patch + EDITOR=/bin/true dpkg-source --commit . $patch +done + +# patch(1) applies with offset and fuzz, so check the result instead of trusting +# the exit code alone +for marker in "s16->silent" "dsd_frame_bits" +do + if ! grep -q "$marker" src/pcm/pcm_meter.c + then + echo "${RED}Error: $marker missing after patching, the patches did not apply as expected${NORMAL}" + exit 1 + fi +done # set debian local suffix flag DEBFULLNAME=$DEBFULLNAME DEBEMAIL=$DEBEMAIL dch --local $DEBSUFFIX "Added PCM meter scope patches: no abort on unconvertible formats, DSD levels" From e8428a6f91064e98e80a69dbbc0a9fa732837ba0 Mon Sep 17 00:00:00 2001 From: Julien Gainza Date: Fri, 24 Jul 2026 10:26:57 +0200 Subject: [PATCH 3/3] [moode-player] Freeze the patched alsa-lib, with a way out The rebuilt alsa-lib was not in the freeze list, so a trixie point update would quietly replace it: dpkg orders 1.2.14-1moode1 below 1.2.14-1+deb13u1, and the meter scope fixes would disappear on an ordinary apt upgrade. Holding it unconditionally has the opposite problem - the freeze outlives its reason and pins libasound2 to an old version for good. So an entry in moode-apt-mark.conf can now carry a condition, " ", checked against the candidate version: while it holds the package stays frozen, once it stops holding the package is released instead. Entries without a condition behave exactly as before, and unhold still releases everything. The alsa-lib entries are unconditional because the recipe already retires itself: on a base OS whose alsa-lib carries the fixes it builds nothing, the package is never installed, and these entries are skipped. The condition is there for an in-place upgrade, the one case that does not go through a fresh image. Co-Authored-By: Claude Opus 4.8 --- packages/moode-player/moode-apt-mark | 53 ++++++++++++++++++++--- packages/moode-player/moode-apt-mark.conf | 8 ++++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/packages/moode-player/moode-apt-mark b/packages/moode-player/moode-apt-mark index 39f27fe..6d8e279 100755 --- a/packages/moode-player/moode-apt-mark +++ b/packages/moode-player/moode-apt-mark @@ -19,16 +19,48 @@ fi cmd=$1 pkgstr="" +releasestr="" package_list="/etc/moode-apt-mark.conf" +# A package entry can carry an optional condition: " ". +# The hold then lasts only while the candidate version still satisfies it, so a +# package frozen while waiting for a fix to reach the distro releases itself once +# the distro ships that fix. Entries without a condition are held as before. +function hold_still_needed { + local package=$1 + local relation=$2 + local version=$3 + local candidate + + case "$relation" in + "<<"|"<="|"="|">="|">>") ;; + *) + echo "WARNING: $package has an unknown condition '$relation', keeping the hold" + return 0 + ;; + esac + + candidate=$(LC_ALL=C apt-cache policy "$package" | awk '/Candidate:/ {print $2; exit}') + if [[ -z "$candidate" ]] || [[ "$candidate" == "(none)" ]]; then + echo "WARNING: no candidate version for $package, keeping the hold" + return 0 + fi + + dpkg --compare-versions "$candidate" "$relation" "$version" +} + echo "Generating package list" while IFS= read -r line do - package=$(echo "$line" | sed -e 's/[[:space:]]*#.*// ; /^[[:space:]]*$/d') - if [[ -n "$package" ]]; then + entry=$(echo "$line" | sed -e 's/[[:space:]]*#.*// ; /^[[:space:]]*$/d') + if [[ -n "$entry" ]]; then + read -r package relation version <<< "$entry" result=$(dpkg-query --showformat='${Version}\n' --show $package) if [[ -z $result ]]; then echo "INFO: $package skipped, not installed" + elif [[ "$cmd" == "hold" ]] && [[ -n "$relation" ]] && ! hold_still_needed "$package" "$relation" "$version"; then + echo "INFO: $package no longer needs to be frozen, releasing it" + releasestr+="$package " else pkgstr+="$package " if [ $? -gt 1 ]; then @@ -40,8 +72,17 @@ do done < "$package_list" echo "Marking packages" -apt-mark "$cmd" $pkgstr -if [ $? -gt 1 ]; then - echo "ERROR: apt-mark $cmd failed, exiting script" - exit 1 +if [[ -n "$pkgstr" ]]; then + apt-mark "$cmd" $pkgstr + if [ $? -gt 1 ]; then + echo "ERROR: apt-mark $cmd failed, exiting script" + exit 1 + fi +fi +if [[ -n "$releasestr" ]]; then + apt-mark unhold $releasestr + if [ $? -gt 1 ]; then + echo "ERROR: apt-mark unhold failed, exiting script" + exit 1 + fi fi diff --git a/packages/moode-player/moode-apt-mark.conf b/packages/moode-player/moode-apt-mark.conf index 3589850..beaadff 100644 --- a/packages/moode-player/moode-apt-mark.conf +++ b/packages/moode-player/moode-apt-mark.conf @@ -19,6 +19,14 @@ chromium-codecs-ffmpeg-extra #chromium-sandbox #rpi-chromium-mods # +# Rebuilt with the PCM meter scope fixes (alsa-project/alsa-lib #516 and #517), +# so the distro's own package must not replace it - a trixie point update to +# 1.2.14-1+deb13u1 already outranks 1.2.14-1moode1. On a base OS whose alsa-lib +# carries the fixes the recipe builds nothing, the package is not installed and +# these two lines are skipped; on one that does not, append the release that +# fixes it as a condition ("libasound2t64 << 1.2.17") to lift the freeze. +libasound2-data +libasound2t64 libasound2-plugin-bluez librespot mediainfo