diff --git a/bin/omarchy-audio-tuning b/bin/omarchy-audio-tuning index c7a73125..f238de46 100755 --- a/bin/omarchy-audio-tuning +++ b/bin/omarchy-audio-tuning @@ -41,6 +41,29 @@ sink_matching() { pactl list sinks short 2>/dev/null | awk -v p="$1" '$2 ~ p {print $2; exit}' } +# Dell keys its Cirrus speaker firmware on the DMI product SKU, which makes it the +# most precise identifier available for these machines -- narrower than a product +# name, and it distinguishes models whose names differ only by marketing. Compared +# case-insensitively against an exact SKU, never a substring, so a tuning cannot +# accidentally widen to a whole product line. +sku_matches() { + local sku want + sku="$(cat /sys/class/dmi/id/product_sku 2>/dev/null)" + [[ -n $sku ]] || return 1 + for want in "$@"; do + [[ ${sku,,} == "${want,,}" ]] && return 0 + done + return 1 +} + +dmi_matches() { + local want + for want in "$@"; do + omarchy-hw-match "$want" 2>/dev/null && return 0 + done + return 1 +} + # Print the tuning directory matching this laptop, if any. Matching is data, not # code: a tuning declares the DMI string it belongs to and the sink it expects, so # most tunings can be added as a directory with no new script. A tuning whose @@ -50,7 +73,7 @@ tuning_match() { for dir in "$tunings_dir"/*/; do [[ -r $dir/tuning.conf ]] || continue - unset match_dmi match_command sink_pattern + unset match_dmi match_sku match_command sink_pattern # shellcheck disable=SC1090 source "$dir/tuning.conf" @@ -58,11 +81,16 @@ tuning_match() { # the ISO chroot with no audio server, and a match that depended on a present # sink would come back empty there -- so the machine would get neither the LV2 # dependency nor the tuning, and nothing would retry. + # A tuning may list several models it has been validated on. match_dmi and + # match_sku are arrays, so a plain string still works as a single entry. if [[ -n ${match_command:-} ]]; then "$match_command" 2>/dev/null || continue + elif [[ -n ${match_sku:-} ]]; then + sku_matches "${match_sku[@]}" || continue + elif [[ -n ${match_dmi:-} ]]; then + dmi_matches "${match_dmi[@]}" || continue else - [[ -n ${match_dmi:-} ]] || continue - omarchy-hw-match "$match_dmi" 2>/dev/null || continue + continue fi # Required whichever way the tuning matched: the graph's target sink is diff --git a/default/audio/tunings/dell-xps14-da14260/filter-chain.conf b/default/audio/tunings/dell-xps-2026/filter-chain.conf similarity index 97% rename from default/audio/tunings/dell-xps14-da14260/filter-chain.conf rename to default/audio/tunings/dell-xps-2026/filter-chain.conf index 28bbaf65..04810a8f 100644 --- a/default/audio/tunings/dell-xps14-da14260/filter-chain.conf +++ b/default/audio/tunings/dell-xps-2026/filter-chain.conf @@ -1,10 +1,13 @@ -# Dell XPS 14 DA14260 speaker tuning. +# Dell XPS 14 / XPS 16 (2026) speaker tuning. # # Biquad chain fitted to the measured response of the xps-audio-linux EasyEffects # profile under a dense pink-weighted multitone of 104 bin-aligned tones, # followed by a lookahead limiter. Measures 1.24 dB RMS against that reference # (0.97 dB weighted over the fit's own error metric). # +# Fitted and measured on the XPS 14 (SKU 0DB9); the XPS 16 (0DBA) is covered on +# report that the same profile suits it. See tuning.conf. +# # Q below 200 Hz is capped at 1.8 on purpose. A closer magnitude fit is possible # with high-Q sections, but the reference produces its narrow bass features by # convolution, and reproducing them with high-Q biquads swung group delay 31 ms diff --git a/default/audio/tunings/dell-xps14-da14260/tuning.conf b/default/audio/tunings/dell-xps-2026/tuning.conf similarity index 60% rename from default/audio/tunings/dell-xps14-da14260/tuning.conf rename to default/audio/tunings/dell-xps-2026/tuning.conf index 26c43be8..e7bb633b 100644 --- a/default/audio/tunings/dell-xps14-da14260/tuning.conf +++ b/default/audio/tunings/dell-xps-2026/tuning.conf @@ -1,15 +1,19 @@ -## Dell XPS 14 DA14260 internal speakers. +## Dell XPS 14 / XPS 16 (2026) internal speakers. ## ## Thirteen biquads and a lookahead limiter, applied as a PipeWire filter-chain ## in front of the internal speaker sink. The stock Linux path already loads ## Dell's Cirrus smart-amplifier firmware; this adds the perceptual voicing the ## Windows Waves layer provides and Linux does not. -description="Dell XPS 14 (2026) speakers" -## Matched on the DMI product name plus the presence of the sink below, which -## together are specific enough that no per-model predicate script is needed. -## Hardware needing a sharper test can set match_command to any predicate. -match_dmi="XPS 14 DA14260" +description="Dell XPS 14/16 (2026) speakers" +## Matched on the DMI product SKU, which is what Dell keys the Cirrus speaker +## firmware on -- 10280db9 for the XPS 14 and 10280dba for the XPS 16 -- so it +## identifies the speaker hardware itself rather than a marketing name. Compared as +## whole values, so this cannot widen to the rest of the XPS line. +## +## 0DB9 XPS 14 -- measured here, see below +## 0DBA XPS 16 -- included on report that this profile suits it, not measured +match_sku=("0DB9" "0DBA") ## Unescaped dots: this is passed to awk as a string, where a backslash escape ## would be consumed before the regex sees it. sink_pattern='^alsa_output.*sof_sdw.*HiFi__Speaker__sink$' @@ -22,6 +26,10 @@ sink_pattern='^alsa_output.*sof_sdw.*HiFi__Speaker__sink$' derived_from="xps-audio-linux xps-clone (MIT, spencerbull)" validated_by="dhh" validated_on="2026-07-24" +## The measurements below were taken on the XPS 14 (0DB9). The XPS 16 (0DBA) is +## covered on report rather than measurement; re-measure there before treating +## these figures as describing it. +validated_hardware="XPS 14 DA14260 (0DB9)" ## Measured against that reference under a dense pink-weighted multitone of 104 ## bin-aligned tones. See docs/AUDIO-TUNING.md for how to reproduce these. diff --git a/docs/AUDIO-TUNING.md b/docs/AUDIO-TUNING.md index e2876725..8f24917a 100644 --- a/docs/AUDIO-TUNING.md +++ b/docs/AUDIO-TUNING.md @@ -43,14 +43,29 @@ and the sink-listing scripts rather than for daily use. ## Adding a tuning Add a directory with a `tuning.conf` and a `filter-chain.conf`. No new command is -needed: matching is data. A tuning declares `match_dmi` (checked against the DMI -product name and family) and `sink_pattern`, and needing both is specific enough -for most hardware. If yours needs a sharper test, set `match_command` to any -predicate instead — an `omarchy-hw-*` script, for example. `sink_pattern` is -required either way, since the graph's target sink is substituted from it. +needed: matching is data. A tuning declares how to recognise its hardware, plus the +`sink_pattern` its graph targets: + +| Key | Matches on | Notes | +|---|---|---| +| `match_sku` | DMI product SKU, whole value | Most precise. Vendors key speaker firmware on it, so it identifies the hardware rather than a marketing name | +| `match_dmi` | DMI product name or family, substring | Convenient, but a short string widens fast — `product_family` here is `Dell Laptops` | +| `match_command` | Any predicate you name | For hardware needing a sharper test than either | + +`match_sku` and `match_dmi` are lists, so one tuning can cover several models it has +been validated on: + +```bash +match_sku=("0DB9" "0DBA") # XPS 14 and XPS 16 +``` + +`sink_pattern` is required whichever method you use, since the graph's target sink +is substituted from it. Gate narrowly and widen as models are validated; a tuning aimed at the wrong -drivers can sound worse than none and can stress them. +drivers can sound worse than none and can stress them. When a tuning covers a model +it was not measured on, say so in `tuning.conf` — the provenance fields are there to +keep that distinction visible rather than implied. Two hard requirements: