feat: add user_eq override functionality and update documentation
This commit introduces support for overriding the user_eq settings via a local user_eq.json file, allowing users to customize their audio profile without modifying the committed graph.json. The change includes: - Adding user_eq.json to .gitignore - Updating apply.sh to merge user_eq.json contents into the effective graph when present - Rewriting INSTALL.md and README.md to document the new override workflow - Enhancing preflight checks in apply.sh to use the merged graph - Providing clear instructions for both override file and direct editing approaches The new approach allows users to maintain custom EQ settings across git pulls while preserving the ability to edit graph.json directly when needed.
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,5 @@
|
||||
# Bankstown source checkout + build output from install-deps.sh
|
||||
/build/
|
||||
|
||||
# Local user_eq override consumed by apply.sh (copy from user_eq.example.json)
|
||||
/user_eq.json
|
||||
|
||||
35
INSTALL.md
35
INSTALL.md
@@ -147,16 +147,19 @@ If you have your own recalibrated FIRs, drop them at that path (or edit the six
|
||||
./apply.sh
|
||||
```
|
||||
|
||||
`apply.sh` preflights before touching anything:
|
||||
`apply.sh`:
|
||||
|
||||
1. validates `graph.json` is well-formed JSON (`python3` or `jq`)
|
||||
2. checks the `/usr/share/t2-linux-audio/15_1/` dir exists (§1)
|
||||
3. checks every FIR `.wav` **referenced by the graph** is present (§4)
|
||||
4. checks every LV2 plugin URI **the graph loads** resolves in `lv2ls` (§3)
|
||||
2. **builds the effective graph** → `~/.audiograph.json`: if `user_eq.json`
|
||||
exists next to the script, its contents replace the `user_eq` node's
|
||||
`control` block (`jq`); otherwise it's a straight copy of `graph.json` (§7)
|
||||
3. checks `/usr/share/t2-linux-audio/15_1/` exists (§1)
|
||||
4. checks every FIR `.wav` **referenced by the graph** is present (§4)
|
||||
5. checks every LV2 plugin URI **the graph loads** resolves in `lv2ls` (§3)
|
||||
|
||||
then `sudo cp`s the graph into place, restarts WirePlumber, and confirms the
|
||||
sink came up. Steps 3–4 read the paths/URIs straight out of `graph.json`, so
|
||||
they stay correct if you edit it.
|
||||
then `sudo cp`s `~/.audiograph.json` into place, restarts WirePlumber, and
|
||||
confirms the sink came up. Steps 4–5 read the paths/URIs straight out of the
|
||||
merged graph, so they stay correct if you edit either file.
|
||||
|
||||
Pass `-f` to skip the preflight (JSON validation still runs):
|
||||
|
||||
@@ -186,11 +189,19 @@ pw-top # look for the filter-chain node, check for XRUN
|
||||
|
||||
## 7. Pick a sound profile
|
||||
|
||||
`user_eq` is the first node in the graph and the only block meant for
|
||||
hand-editing — an 8-band tone control that defaults flat (= the reference
|
||||
voicing). Paste one of the preset rows from
|
||||
[README.md § User preference EQ](README.md#user-preference-eq) into its `control`
|
||||
block and re-run `./apply.sh`.
|
||||
`user_eq` is the first node in the graph — an 8-band tone control that defaults
|
||||
flat (= the reference voicing). To change it without touching the committed
|
||||
`graph.json`:
|
||||
|
||||
```sh
|
||||
cp user_eq.example.json user_eq.json # git-ignored
|
||||
$EDITOR user_eq.json # set the g_* values
|
||||
./apply.sh # merges it into ~/.audiograph.json and installs
|
||||
```
|
||||
|
||||
`jq` must be installed for this path. Delete `user_eq.json` to return to the
|
||||
default. Preset values (Rock, Classical, Movie–dialogue, …) are in
|
||||
[README.md § User preference EQ](README.md#user-preference-eq).
|
||||
|
||||
---
|
||||
|
||||
|
||||
20
README.md
20
README.md
@@ -205,9 +205,23 @@ compressed if pushed hard.
|
||||
| 6 | 6 kHz | bell | detail / sibilance |
|
||||
| 7 | 10 kHz | high shelf | air |
|
||||
|
||||
Edit the `g_*` values in the `user_eq` `control` block. **Linear, not dB**
|
||||
(`+3 dB ≈ 1.41`, `−3 dB ≈ 0.71`). Keep each between `0.5` (−6 dB) and `2.0`
|
||||
(+6 dB). Run `./apply.sh` after.
|
||||
Values are **linear, not dB** (`+3 dB ≈ 1.41`, `−3 dB ≈ 0.71`). Keep each `g_*`
|
||||
between `0.5` (−6 dB) and `2.0` (+6 dB).
|
||||
|
||||
Two ways to set it, both followed by `./apply.sh`:
|
||||
|
||||
- **Override file (recommended, survives `git pull`).** Copy the template and
|
||||
edit it:
|
||||
```sh
|
||||
cp user_eq.example.json user_eq.json
|
||||
$EDITOR user_eq.json
|
||||
```
|
||||
`user_eq.json` is git-ignored. When present, `apply.sh` splices its contents
|
||||
into the `user_eq` node's `control` block with `jq`, writes the result to
|
||||
`~/.audiograph.json`, and installs that. Delete `user_eq.json` to go back to
|
||||
the committed default.
|
||||
- **Edit `graph.json` directly** — change the `g_*` in the `user_eq` `control`
|
||||
block. Simple, but a `git pull` will conflict.
|
||||
|
||||
### Presets — the 8 `g_*` values, `g_0`…`g_7`
|
||||
|
||||
|
||||
66
apply.sh
66
apply.sh
@@ -1,14 +1,21 @@
|
||||
#!/bin/bash
|
||||
# Apply the MacBook Pro 15,1 audio DSP graph:
|
||||
# validate JSON, preflight the prerequisites, install, reload WirePlumber.
|
||||
# build the effective graph, preflight the prerequisites, install, reload.
|
||||
#
|
||||
# ./apply.sh full checks
|
||||
# ./apply.sh -f skip the preflight checks (still validates JSON)
|
||||
# ./apply.sh validate + preflight + build + install + reload
|
||||
# ./apply.sh -f skip the preflight checks (JSON validation still runs)
|
||||
#
|
||||
# If user_eq.json exists next to this script, its contents replace the
|
||||
# "user_eq" node's control block (requires jq) - copy user_eq.example.json to
|
||||
# user_eq.json and edit. The merged graph is written to ~/.audiograph.json and
|
||||
# that file is what gets installed.
|
||||
|
||||
set -u
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
GRAPH_SRC="$SCRIPT_DIR/graph.json"
|
||||
OVERRIDE="$SCRIPT_DIR/user_eq.json"
|
||||
MERGED="$HOME/.audiograph.json"
|
||||
GRAPH_DST="/usr/share/t2-linux-audio/15_1/graph.json"
|
||||
|
||||
FORCE=0
|
||||
@@ -17,38 +24,55 @@ case "${1:-}" in -f|--force|--no-check) FORCE=1 ;; esac
|
||||
have() { command -v "$1" >/dev/null 2>&1; }
|
||||
die() { echo "Error: $*" >&2; exit 1; }
|
||||
|
||||
# 0 = valid, 1 = invalid, 2 = no validator available
|
||||
json_ok() {
|
||||
if have python3; then python3 -m json.tool "$1" >/dev/null 2>&1
|
||||
elif have jq; then jq -e . "$1" >/dev/null 2>&1
|
||||
else return 2; fi
|
||||
}
|
||||
|
||||
[ -f "$GRAPH_SRC" ] || die "$GRAPH_SRC not found"
|
||||
|
||||
# 1. valid JSON -----------------------------------------------------------
|
||||
if have python3; then
|
||||
python3 -m json.tool "$GRAPH_SRC" >/dev/null || die "graph.json is not valid JSON"
|
||||
echo "ok: graph.json is valid JSON"
|
||||
elif have jq; then
|
||||
jq -e . "$GRAPH_SRC" >/dev/null || die "graph.json is not valid JSON"
|
||||
echo "ok: graph.json is valid JSON"
|
||||
json_ok "$GRAPH_SRC"; rc=$?
|
||||
[ "$rc" -eq 1 ] && die "graph.json is not valid JSON"
|
||||
[ "$rc" -eq 2 ] && echo "warn: no python3/jq - skipping JSON validation"
|
||||
[ "$rc" -eq 0 ] && echo "ok: graph.json is valid JSON"
|
||||
|
||||
# --- build the effective graph -> ~/.audiograph.json --------------------
|
||||
if [ -f "$OVERRIDE" ]; then
|
||||
have jq || die "$OVERRIDE exists but jq is not installed"
|
||||
jq -e . "$OVERRIDE" >/dev/null 2>&1 || die "$OVERRIDE is not valid JSON"
|
||||
jq -e 'any(.["filter.graph"].nodes[]; .name == "user_eq")' "$GRAPH_SRC" >/dev/null \
|
||||
|| die "graph.json has no node named user_eq to override"
|
||||
jq --slurpfile ov "$OVERRIDE" \
|
||||
'(.["filter.graph"].nodes[] | select(.name == "user_eq") | .control) = $ov[0]' \
|
||||
"$GRAPH_SRC" > "$MERGED" || die "jq merge failed"
|
||||
echo "ok: merged user_eq.json -> $MERGED"
|
||||
else
|
||||
echo "warn: no python3/jq - skipping JSON validation"
|
||||
cp "$GRAPH_SRC" "$MERGED"
|
||||
echo "ok: no user_eq.json - graph.json as-is -> $MERGED"
|
||||
fi
|
||||
|
||||
json_ok "$MERGED"; rc=$?
|
||||
[ "$rc" -eq 1 ] && die "merged graph $MERGED is not valid JSON"
|
||||
|
||||
# --- preflight (against the merged graph) ------------------------------
|
||||
if [ "$FORCE" -eq 0 ]; then
|
||||
# 2. t2 speaker-DSP package present --------------------------------
|
||||
[ -d "$(dirname "$GRAPH_DST")" ] || die \
|
||||
"$(dirname "$GRAPH_DST") is missing - install the t2 speaker-DSP package first (INSTALL.md section 1)"
|
||||
|
||||
# 3. FIR files referenced by the graph exist ----------------------
|
||||
miss=0
|
||||
while IFS= read -r w; do
|
||||
[ -f "$w" ] || { echo " missing FIR: $w"; miss=1; }
|
||||
done < <(grep -oE '/[^" ]+\.wav' "$GRAPH_SRC" | sort -u)
|
||||
done < <(grep -oE '/[^" ]+\.wav' "$MERGED" | sort -u)
|
||||
[ "$miss" -eq 0 ] && echo "ok: FIR .wav files present" \
|
||||
|| die "FIR files missing - INSTALL.md section 4"
|
||||
|
||||
# 4. every LV2 plugin URI the graph loads resolves ---------------
|
||||
if have lv2ls; then
|
||||
miss=0
|
||||
while IFS= read -r u; do
|
||||
lv2ls | grep -qxF "$u" || { echo " missing plugin: $u"; miss=1; }
|
||||
done < <(grep '"plugin"' "$GRAPH_SRC" | grep -oE 'https?://[^"]+' | sort -u)
|
||||
done < <(grep '"plugin"' "$MERGED" | grep -oE 'https?://[^"]+' | sort -u)
|
||||
[ "$miss" -eq 0 ] && echo "ok: all LV2 plugins resolve" \
|
||||
|| die "LV2 plugins missing - run ./install-deps.sh (INSTALL.md section 3)"
|
||||
else
|
||||
@@ -56,18 +80,18 @@ if [ "$FORCE" -eq 0 ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# 5. install ------------------------------------------------------------
|
||||
echo "Copying graph.json -> $GRAPH_DST"
|
||||
sudo cp "$GRAPH_SRC" "$GRAPH_DST"
|
||||
# --- install ----------------------------------------------------------
|
||||
echo "Installing $MERGED -> $GRAPH_DST"
|
||||
sudo cp "$MERGED" "$GRAPH_DST"
|
||||
|
||||
echo "Restarting WirePlumber"
|
||||
systemctl --user restart wireplumber
|
||||
|
||||
# 6. confirm ----------------------------------------------------------
|
||||
# --- confirm --------------------------------------------------------
|
||||
sleep 1
|
||||
if have wpctl && wpctl status 2>/dev/null | grep -qi "DSP Speakers"; then
|
||||
echo "Done - 'MacBook Pro 15,1 DSP Speakers' sink is up."
|
||||
else
|
||||
echo "Done - graph copied, WirePlumber restarted."
|
||||
echo "Done - graph installed, WirePlumber restarted."
|
||||
echo "If no 'DSP Speakers' sink shows: journalctl --user -u wireplumber -b -e"
|
||||
fi
|
||||
|
||||
@@ -46,6 +46,13 @@ lv2_have http://plugin.org.uk/swh-plugins/fastLookaheadLimiter && need_swh=0
|
||||
pkgs=()
|
||||
[ "$need_lsp" -eq 1 ] && pkgs+=(lsp-plugins)
|
||||
[ "$need_swh" -eq 1 ] && pkgs+=(swh-plugins)
|
||||
have jq || pkgs+=(jq) # apply.sh needs jq for the user_eq override
|
||||
if ! have lv2ls; then
|
||||
case "$PM" in
|
||||
apt) pkgs+=(lilv-utils) ;;
|
||||
dnf|zypper|pacman) pkgs+=(lilv) ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ "${#pkgs[@]}" -gt 0 ]; then
|
||||
if [ -z "$PM" ]; then
|
||||
|
||||
11
user_eq.example.json
Normal file
11
user_eq.example.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"enabled": 1, "mode": 0, "g_in": 1.0, "g_out": 1.0,
|
||||
"ft_0": 5, "f_0": 70.0, "g_0": 1.0, "q_0": 0.7, "s_0": 0,
|
||||
"ft_1": 1, "f_1": 110.0, "g_1": 1.0, "q_1": 1.0,
|
||||
"ft_2": 1, "f_2": 220.0, "g_2": 1.0, "q_2": 1.0,
|
||||
"ft_3": 1, "f_3": 450.0, "g_3": 1.0, "q_3": 1.0,
|
||||
"ft_4": 1, "f_4": 1000.0, "g_4": 1.0, "q_4": 1.0,
|
||||
"ft_5": 1, "f_5": 2500.0, "g_5": 1.0, "q_5": 1.0,
|
||||
"ft_6": 1, "f_6": 6000.0, "g_6": 1.0, "q_6": 1.0,
|
||||
"ft_7": 3, "f_7": 10000.0, "g_7": 1.0, "q_7": 0.7, "s_7": 0
|
||||
}
|
||||
Reference in New Issue
Block a user