diff --git a/.gitignore b/.gitignore index f2cc41a..4b4725d 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/INSTALL.md b/INSTALL.md index 9a0b2c6..e638a0b 100644 --- a/INSTALL.md +++ b/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). --- diff --git a/README.md b/README.md index 221b5b1..ff3727d 100644 --- a/README.md +++ b/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` diff --git a/apply.sh b/apply.sh index 90c5e4d..d0e5f4c 100755 --- a/apply.sh +++ b/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 diff --git a/install-deps.sh b/install-deps.sh index 58c834a..7472028 100755 --- a/install-deps.sh +++ b/install-deps.sh @@ -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 diff --git a/user_eq.example.json b/user_eq.example.json new file mode 100644 index 0000000..445e63b --- /dev/null +++ b/user_eq.example.json @@ -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 +}