47 Commits

Author SHA1 Message Date
Stephen Schuresko
5dd64c531d feat: adjust output gain and ceiling parameters
for improved audio dynamics
2026-09-15 09:00:58 -04:00
mynameisdeleted
e330eeb495 feat: refine user EQ settings for enhanced audio performance 2026-09-12 10:51:30 -04:00
mynameisdeleted
00ed8acc57 feat: update user EQ settings for consistent audio dynamics 2026-09-12 10:47:37 -04:00
mynameisdeleted
ddc905558f feat: adjust audio processing parameters for improved sound dynamics 2026-09-12 10:44:40 -04:00
mynameisdeleted
bace1ea0b8 feat: update user EQ settings for improved audio dynamics 2026-09-12 10:43:36 -04:00
mynameisdeleted
1f9c86bbde feat: adjust audio processing parameters for improved sound quality 2026-09-12 10:39:58 -04:00
mynameisdeleted
4955e01299 feat: update user EQ settings for enhanced audio performance 2026-09-12 10:35:01 -04:00
mynameisdeleted
0a7c917314 feat: adjust limiter settings for improved audio dynamics 2026-09-12 10:32:28 -04:00
mynameisdeleted
ef6b91f31a feat: update filter parameters for improved audio processing 2026-09-12 10:29:35 -04:00
mynameisdeleted
0f4d93c111 feat: add new audio processing graph for MacBook Pro 15,1 DSP Speakers 2026-09-07 12:10:49 -04:00
mynameisdeleted
20f52cc860 feat: remove tweeter thermal guard and update signal routing for improved audio processing 2026-09-07 12:00:58 -04:00
mynameisdeleted
bd39f91b3d feat: adjust gain for filter parameters to enhance audio output 2026-09-07 11:14:26 -04:00
mynameisdeleted
42b33e43aa feat: adjust output gain for improved audio balance 2026-09-07 11:14:03 -04:00
mynameisdeleted
d9408b3d0e feat: increase gain for tweeter channels to enhance audio output 2026-09-07 11:13:14 -04:00
mynameisdeleted
66fed3ccfb . 2026-09-06 21:28:25 -04:00
mynameisdeleted
5f156b63ce feat: enhance thermal guard parameters for improved audio performance 2026-09-04 17:35:20 -04:00
mynameisdeleted
7df6a8f83b feat: update thermal guard parameters for optimized audio performance 2026-09-04 17:31:17 -04:00
mynameisdeleted
922a08d138 feat: adjust thermal guard parameters for enhanced audio performance 2026-09-04 17:28:54 -04:00
mynameisdeleted
b50f9b92e1 feat: reorder thermal guard connections for improved signal flow 2026-09-04 17:26:24 -04:00
mynameisdeleted
b635df23f6 feat: adjust thermal guard parameters for improved audio performance 2026-09-04 17:23:09 -04:00
mynameisdeleted
77f3eebf44 feat: update thermal guard parameters and adjust signal routing in graph configuration 2026-09-04 17:22:43 -04:00
mynameisdeleted
f16065ee20 feat: update thermal guard parameters for improved audio performance 2026-09-04 11:51:06 -04:00
mynameisdeleted
e48b086c22 feat: adjust thermal guard parameters for improved audio performance 2026-09-04 11:49:10 -04:00
mynameisdeleted
6305d597a1 feat: refine thermal guard parameters for improved audio performance 2026-09-04 11:48:09 -04:00
mynameisdeleted
8eb1397043 feat: apply Option A linear forward path topology with wlim and tlim as final output stages 2026-09-04 11:43:52 -04:00
mynameisdeleted
2e695198e5 feat: update thermal guard parameters for improved audio performance and adjust signal routing in graph configuration 2026-09-04 11:19:43 -04:00
mynameisdeleted
d52e48dce1 feat: adjust thermal guard parameters for tweeter and woofer to improve performance and safety 2026-09-04 11:12:30 -04:00
mynameisdeleted
7aea4a7367 feat: set tweeter thermal guard to 100mW (al=0.126491 / -17.96dBFS) with 2.0s fast-decay glitch recovery 2026-09-04 11:07:12 -04:00
mynameisdeleted
d80fd810f5 feat: update tweeter thermal guard parameters to 100mW and adjust RMS threshold in graph configurations 2026-09-04 11:04:26 -04:00
mynameisdeleted
e006be4f8b feat: update woofer thermal guard to 1.0W continuous RMS limit (al=0.40 / -7.96dBFS) and reflect in README 2026-09-04 11:00:43 -04:00
mynameisdeleted
9c4c618b72 feat: add graph configuration for MacBook Pro 15,1 DSP speakers with thermal guards 2026-09-04 10:59:42 -04:00
mynameisdeleted
c44e1081be repair instructions for the adventurous or unfortunate 2026-09-04 10:55:30 -04:00
mynameisdeleted
7e89fb631e docs: note that power users tuning aggressively higher can use iFixit and cheap OEM parts for speaker swaps 2026-09-04 10:55:30 -04:00
mynameisdeleted
16538a0f46 docs: update CAUTION disclaimer to emphasize loud signal risks and best-effort warranty policy 2026-09-04 10:55:30 -04:00
mynameisdeleted
2bcb215725 feat: add dual woofer (500mW) & tweeter (75mW) virtual thermal guards and document in README with disclaimer 2026-09-04 10:55:30 -04:00
22863af6ff Update README.md 2026-09-03 18:54:30 -04:00
mynameisdeleted
225ba3f467 feat: change volume scale from cubic to linear for improved control 2026-09-03 11:52:04 -04:00
mynameisdeleted
a681681fe3 feat: increase gain levels for improved audio output 2026-09-03 11:46:25 -04:00
mynameisdeleted
b88ad5b696 feat: increase gain levels for improved audio output 2026-09-03 11:44:56 -04:00
mynameisdeleted
cc044b4cd2 feat: remove limiter plugin and adjust signal routing for improved audio processing 2026-09-03 11:41:48 -04:00
mynameisdeleted
f5bbf2beb9 feat: update output gain and add new DSP parameters for enhanced audio processing 2026-09-03 11:36:18 -04:00
mynameisdeleted
acde5c460e feat: add additional DSP parameters for enhanced audio processing 2026-09-02 21:11:13 -04:00
mynameisdeleted
4f13afc098 feat: adjust input gain for improved audio performance 2026-09-02 07:38:52 -04:00
mynameisdeleted
6f88e094b8 feat: update EQ parameters for improved audio balance and performance 2026-09-02 07:36:55 -04:00
mynameisdeleted
211545f5d1 style: add additional comments in user_eq.example.json for enhanced user guidance 2026-09-02 05:09:23 -04:00
mynameisdeleted
ce8fd1ae20 style: enhance user_eq.example.json with detailed comments for better user guidance 2026-09-02 05:04:08 -04:00
mynameisdeleted
2894f0dbda feat: update DSP parameters for improved audio performance and gain adjustments
(for f>100)  g_N = sqrt(2)^(log(200/f_N)/log(2))*g_5" (math formula for white noise to pink-oise gain curve)

for f<100  g_N virtualbase takes over and the white-to-pink filter needs to be overridden
2026-09-02 04:59:43 -04:00
18 changed files with 704 additions and 1329 deletions

3
.gitignore vendored
View File

@@ -1,12 +1,11 @@
# Bankstown source checkout + build output from install-deps.sh
/build/
graph_merged.json
# Local user_eq override consumed by apply.sh (copy from user_eq.example.json)
/user_eq.json
# Generated baked single-stage FIR files & simple graph
/15_1/baked-*.wav
laptop-configs/**/baked-*.wav
/baked-*.wav
/graph_simple.json

View File

@@ -1,183 +0,0 @@
# Next-Gen Audio DSP Architecture: Compressed-Chain "Digital Surround" (Active Crossover) Engine & Multi-Laptop Ecosystem
This document provides a comprehensive technical blueprint of the **Next-Gen Audio DSP Engine** implemented in `mbp15-1-audio-dsp`. The system delivers studio-grade, zero-latency audio performance for Linux laptops, outperforming factory OEM macOS/Windows DSP stacks while remaining fully open-source and hardware-portable.
> ### 📻 "Digital Surround" Marketing vs. Active Digital Crossovers
> In consumer laptop marketing, multi-speaker setups are frequently advertised under buzzwords like **"Dolby Atmos"**, **"3D Digital Surround"**, or **"Spatial Audio"** to imply multi-channel cinema sound. In real-world acoustic engineering, this feature is an **Active Multi-Band Digital Crossover Engine**. Rather than blasting a single flat stereo stream through identical tiny speakers, our DSP active crossover splits the audio signal into precise frequency bands—routing deep sub-bass to high-excursion woofers and spatial high frequencies to dedicated tweeters. This discrete multi-driver time and frequency alignment eliminates inter-modulation distortion, expands the physical soundstage, and delivers the immersive room-filling audio experience that consumer brands market as "Digital Surround."
---
## 🏛️ 1. Architectural Highlights
* **⚡ Single-Stage Compressed-Chain FIR Engine:** Collapses all linear time-invariant (LTI) stages—User EQ, Voicing EQ, Linkwitz-Riley crossover high-pass filters, and driver impulse responses—into single composite FIR impulse files.
* **⏱️ 5.0 ms Parallel Lookahead Sidechain Limiting:** Generates parallel advance control taps (`baked-lookahead-*.wav`) that feed peak warnings to post-convolver limiters **5.0 ms ahead of the physical audio**, adding **0.0 ms of buffer delay** to the listener's main audio path.
* **🎵 VirtualBass-First Topology:** Positions Bankstown psychoacoustic harmonic excitation **first** in the processing chain, capturing 100% of raw sub-bass energy to synthesize 2nd and 3rd order harmonics before any crossover cuts.
* **🔒 True-Peak (ISP) Guarding:** 4x oversampled inter-sample peak estimation normalized to `-0.5 dBFS` ceiling to prevent digital DAC clipping and inter-sample distortion.
* **💻 DMI SMBIOS Hardware Auto-Detection:** Dynamically queries Linux `/sys/class/dmi/id/` sysfs metadata to match and load laptop profiles from `laptop-configs/<vendor>/<model>/`.
* **🔄 Zero-Drop Stream Migration:** Integrates `pactl move-sink-input` and PipeWire `capture.props` volume control to preserve active application streams (Firefox, Spotify, Chrome) and lock hardware volume keys (F11/F12 / Touch Bar) across restarts.
---
## 🔬 2. Compressed-Chain Single-Stage FIR Engine
### 2.1 LTI Stage Collapsing
Traditional laptop DSP pipelines cascade 10 to 20 separate IIR biquads and convolver stages, consuming heavy CPU cycles and adding phase distortion. The Next-Gen Engine uses standard-library Python ([`bake-graph.py`](file:///home/steve/w11/mbp15-1-audio-dsp/bake-graph.py)) to perform single-pass LTI convolution that bakes User EQ, Voicing EQ, and Crossover filters directly into the 4-channel driver convolvers:
$$\text{FIR}_{\text{baked}}(t) = \text{EQ}_{\text{user}}(t) * \text{EQ}_{\text{voicing}}(t) * \text{EQ}_{\text{crossover}}(t) * \text{IR}_{\text{driver}}(t)$$
```mermaid
graph TD
A["Raw Audio Input (FL / FR)"] --> B["Bankstown VirtualBass (2nd/3rd Order Harmonics)"]
B -->|4-Channel Direct Feed| C1["Woofer Left FIR (baked-woofers-*.wav)"]
B -->|4-Channel Direct Feed| C2["Woofer Right FIR (baked-woofers-*.wav)"]
B -->|4-Channel Direct Feed| D1["Tweeter Left FIR (baked-tweeters-*.wav)"]
B -->|4-Channel Direct Feed| D2["Tweeter Right FIR (baked-tweeters-*.wav)"]
C1 --> E1["Woofer Fast Limiter (wlim:in_1)"]
C2 --> E1["Woofer Fast Limiter (wlim:in_2)"]
D1 --> F1["Tweeter Fast Limiter (tlim:in_1)"]
D2 --> F1["Tweeter Fast Limiter (tlim:in_2)"]
E1 --> G1["Woofer Drivers (Left / Right)"]
F1 --> G2["Tweeter Drivers (Left / Right)"]
```
### 2.2 Single-Pass FIR Baking Pipeline Process
The FIR baking process in [`bake-graph.py`](file:///home/steve/w11/mbp15-1-audio-dsp/bake-graph.py) folds static LTI processing stages, latency reduction, target curves, and true-peak guarding directly into composite `.wav` impulse responses:
```mermaid
graph TD
A["Raw Acoustic Driver Measurement<br/>(White-Noise Impulse Response)"] --> B["White-to-Pink Voicing Curve<br/>(-3 dB/octave Tonal Tilt)"]
B --> C["User EQ Curves (user_eq.json)<br/>(Bass Shelves, Peaking EQs, Treble Shelves)"]
C --> D["Crossover Filters<br/>(Linkwitz-Riley High-Pass / Low-Pass)"]
D --> E["Latency Trimming (5.0ms Lead Optimization)"]
E --> F["True-Peak ISP Guarding (-0.5 dBFS Ceiling)"]
F --> G["Tail Resolution Fadeout (2048-sample Cosine Window)"]
G --> H["baked-woofers-*.wav / baked-tweeters-*.wav<br/>(Single-Pass Baked Driver FIRs)"]
```
### 2.3 Post-Convolver Quad-Driver Safety Limiting
To prevent speaker cone over-excursion and thermal overload without altering frequency response:
1. **Direct Terminal Measurement:** Fast lookahead limiters (`wlim` and `tlim`) operate directly on the 4 post-convolver driver channels, accurately measuring the exact equalized waveform present at the driver terminals.
2. **Dedicated Driver Ceilings:** Woofers (`wlim`) and Tweeters (`tlim`) have independent brickwall peak ceilings (`-2 dBFS` for woofers, `-1 dBFS` for tweeters) that guard against hardware driver clipping without squashing tonal dynamics or frequency response balance.
3. **Zero Added Listener Latency:**
* Post-convolver limiters (`wlim` and `tlim`) receive peak warnings 5.0 ms before the audio reaches the speaker drivers.
* **Added buffer latency for the listener: `0.0 ms`.**
---
## 💻 3. Multi-Laptop Profile Hierarchy & Auto-Detection
### 3.1 DMI SMBIOS Hardware Matching
The auto-detection tool ([`detect_hardware.py`](file:///home/steve/w11/mbp15-1-audio-dsp/detect_hardware.py)) queries the Linux Kernel DMI sysfs interface:
* `/sys/class/dmi/id/sys_vendor` (e.g., `Apple Inc.`, `Dell Inc.`, `LENOVO`)
* `/sys/class/dmi/id/product_name` (e.g., `MacBookPro15,1`, `XPS 15 9520`)
`detect_hardware.py` matches these strings against `profile.json` metadata in the profile tree.
### 3.2 Repository Directory Structure (`laptop-configs/`)
```text
laptop-configs/
├── apple/
│ ├── mbp15_1/
│ │ ├── profile.json # DMI matching metadata
│ │ ├── graph.json # Baseline PipeWire DSP topology
│ │ ├── woofers-48k.wav # Raw woofer impulse response
│ │ └── tweeters-48k.wav # Raw tweeter impulse response
│ └── mbp16_1/
│ ├── profile.json
│ ├── graph.json
│ ├── woofers-48k.wav
│ └── tweeters-48k.wav
└── dell/
└── xps15_9520/
├── profile.json
└── ...
```
### 3.3 CLI Flags for `detect_hardware.py`
| Flag | Description |
| :--- | :--- |
| `--list-configs` | Lists all available laptop profiles in `laptop-configs/` with DMI match strings. |
| `--model <name>` | Manually specifies target model (e.g., `mbp15_1` or `xps15_9520`). |
| `--manual` | Interactive prompt for selecting from available profiles. |
| `--json` | Outputs hardware detection details in JSON format. |
| `--quiet` | Suppresses output and prints only the resolved profile directory path. |
---
## 🎛️ 4. Terminal EQ Tuning & Live Graph Merging
### 4.1 Single-Line Formatted `user_eq.json`
The terminal EQ tuning utility ([`eq.py`](file:///home/steve/w11/mbp15-1-audio-dsp/eq.py)) formats [`user_eq.json`](file:///home/steve/w11/mbp15-1-audio-dsp/user_eq.json) with single-line band alignment matching `user_eq.example.json`:
```json
{
"enabled": 1, "mode": 0, "g_in": 1.0, "g_out": 1.50,
"ft_0": 5, "f_0": 70.0 , "g_0": 1.26, "q_0": 0.7, "s_0": 0,
"ft_1": 1, "f_1": 110.0 , "g_1": 1.26, "q_1": 1.0,
"ft_2": 1, "f_2": 315.0 , "g_2": 1.00, "q_2": 1.0,
"ft_3": 1, "f_3": 1000.0 , "g_3": 1.00, "q_3": 1.0,
"ft_4": 1, "f_4": 2500.0 , "g_4": 1.00, "q_4": 1.0,
"ft_5": 1, "f_5": 6000.0 , "g_5": 1.00, "q_5": 1.0,
"ft_6": 3, "f_6": 10000.0, "g_6": 0.89, "q_6": 0.7,
"ft_7": 3, "f_7": 16000.0, "g_7": 0.89, "q_7": 0.7, "s_7": 0
}
```
### 4.2 Unified `user_eq.json` Graph Merging
In [`apply.sh`](file:///home/steve/w11/mbp15-1-audio-dsp/apply.sh#L82-L95), `user_eq.json` is merged into `$GRAPH_SRC` via `jq` for **both `--bake` and un-baked pipelines**:
```bash
if [ -f "$OVERRIDE" ]; then
jq --slurpfile ov "$OVERRIDE" \
'(.["filter.graph"].nodes[] | select(.name == "user_eq") | .control) = $ov[0]' \
"$GRAPH_SRC" > "$MERGED"
fi
```
This guarantees that user EQ gain adjustments and preset switches immediately affect live playback!
---
## 🔀 5. Interoperability & Stream Migration
### 5.1 PipeWire Volume Control Binding
`graph.json` exposes `loudness:volume` control directly inside `capture.props`:
```json
"capture.props": {
"node.name": "audio_effect.t2-151-speakers",
"media.class": "Audio/Sink",
"priority.session": 2500,
"priority.driver": 2500,
"capture.volumes": [
{
"control": "loudness:volume",
"min": -65.0,
"max": 0.0,
"scale": "cubic"
}
]
}
```
This allows desktop volume daemons and hardware media keys (**F11 / F12 / Touch Bar**) to lock directly onto the DSP Speakers virtual sink.
### 5.2 Zero-Drop Stream Migration
Upon cold restart of `wireplumber.service`, [`apply.sh`](file:///home/steve/w11/mbp15-1-audio-dsp/apply.sh#L188-L197) iterates over active PulseAudio/PipeWire sink inputs and migrates them to the new DSP sink:
```bash
if have pactl; then
pactl list short sink-inputs | awk '{print $1}' | while read -r stream_id; do
pactl move-sink-input "$stream_id" "audio_effect.t2-151-speakers"
done
fi
```
Running `./apply.sh` automatically re-links Firefox, YouTube, and Spotify streams with **zero audio drop and zero browser tab reloads required!** 🎧🔊⚡

View File

@@ -1,5 +1,8 @@
# MacBook Pro 15,1 — Warm, Natural Audio DSP for t2linux
based on https://github.com/lemmyg/t2-apple-audio-dsp/tree/master
[![GitHub Repository](https://img.shields.io/badge/Repository-GitHub-181717.svg?logo=github)](https://github.com/mynameisdeleted/mbp15-1-audio-dsp) [![Fairfax Media Git](https://img.shields.io/badge/Repository-Fairfax%20Media-003366.svg)](https://git.fairfaxmedia.net/t2linux/mbp15-1-audio-dsp.git) [![t2linux](https://img.shields.io/badge/Platform-t2linux-blue.svg)](https://wiki.t2linux.org/) [![Asahi Audio Ecosystem](https://img.shields.io/badge/Ecosystem-Asahi%20Audio-orange.svg)](https://github.com/AsahiLinux/asahi-audio) [![PipeWire](https://img.shields.io/badge/Audio-PipeWire%20%2F%20WirePlumber-red.svg)](https://pipewire.org/) [![Target Hardware](https://img.shields.io/badge/Hardware-MacBook%20Pro%2015%2C1-black.svg)]()
A custom PipeWire `filter-chain` DSP graph engineered to deliver warm, natural audio to **t2linux** on the **MacBook Pro 15,1** (2018/2019 Intel T2)—aimed at matching or beating macOS (OS X) audio quality both subjectively and objectively.
@@ -19,6 +22,7 @@ Upstream graphs (`asahi-audio` / `t2-apple-audio-dsp`) target a measurement-flat
| 💥 **Distortion at High Volume** | Post-FIR driver limiters (`wlim` @ -2dB, `tlim` @ -1dB) | Crystal clean output at 100% volume with zero amp clipping |
| 🔊 **Woofer Over-Excursion** | 60 Hz high-pass + 8-band multiband compressor | Woofers don't bottom out or rattle on heavy bass beats |
| 🔇 **No Deep Sub-Bass** | Psychoacoustic sub-bass (`virtualbass` via Bankstown) | Extended perceived low-end without physical cone strain |
| 🛡️ **Voice-Coil Overheating** | Dual Woofer & Tweeter Virtual Thermal Guards | Continuous white noise automatically attenuated to 500mW / 75mW |
| 🎚️ **Fixed / Rigid EQ** | Isolated 8-band `user_eq` preference node | Custom tone presets that survive git updates |
---
@@ -37,8 +41,7 @@ flowchart TD
subgraph Stage2 ["2. Dynamics & Headroom Management"]
EQ --> VB["🔊 Virtual Bass (Bankstown Sub-Harmonics)"]:::dynamics
VB --> MBComp["📊 Multiband Compressor (8-Band LSP)"]:::dynamics
MBComp --> Limiter["🛡️ Main Limiter (Broadband Lookahead)"]:::limiter
Limiter --> LoudComp["👂 Loudness Comp (ISO-226 Equal Loudness)"]:::dynamics
MBComp --> LoudComp["👂 Loudness Comp (ISO-226 2k FFT)"]:::dynamics
end
subgraph Stage3 ["3. Crossover & Driver FIR Correction"]
@@ -47,22 +50,23 @@ flowchart TD
subgraph Tweeters ["Tweeter Channels"]
Copy --> ConvLT["🔊 Tweeter L FIR (convLT)"]:::fir
Copy --> ConvRT["🔊 Tweeter R FIR (convRT)"]:::fir
ConvLT --> TGuard["🛡️ Tweeter Thermal Guard (75mW / 1.0s Tau)"]:::limiter
ConvRT --> TGuard
TGuard --> TLim["🛡️ Tweeter Limiter (-1 dB Ceiling)"]:::limiter
end
subgraph Woofers ["Woofer Channels"]
Copy --> ConvLW["🔊 Woofer L FIR (convLW)"]:::fir
Copy --> ConvRW["🔊 Woofer R FIR (convRW)"]:::fir
ConvLW --> WLim["🛡️ Woofer Limiter (-2 dB Ceiling)"]:::limiter
ConvRW --> WLim
WLim --> WGuard["🛡️ Woofer Thermal Guard (500mW / 2.5s Tau)"]:::limiter
end
end
subgraph Stage4 ["4. Driver Safety Backstops & Output"]
ConvLT --> TLim["🛡️ Tweeter Limiter (-1 dB Ceiling)"]:::limiter
ConvRT --> TLim
ConvLW --> WLim["🛡️ Woofer Limiter (-2 dB Ceiling)"]:::limiter
ConvRW --> WLim
subgraph Stage4 ["4. Driver Output"]
TLim --> Out["🔈 RawSpeakers Sink"]:::input
WLim --> Out
WGuard --> Out
end
classDef input fill:#2d3748,stroke:#4a5568,color:#fff;
@@ -74,6 +78,29 @@ flowchart TD
---
## 🛡️ Dual-Driver Virtual Thermal Guards & Power Caps
Because Linux cannot access the T2 hardware current-sensing ADCs, we implement a **Virtual Voice-Coil Thermal Model** that models continuous electrical power dissipation ($P = V_{\text{rms}}^2 / R_{\text{vc}}$) and exponential cooling into the air gap:
$$C_{\text{th}} \frac{d\Delta T(t)}{dt} = \frac{V_{\text{rms}}^2(t)}{R_{\text{vc}}} - \frac{\Delta T(t)}{R_{\text{th}}}$$
### Thermal Guard Configurations
* **Woofer Thermal Guard (`thermal_guard` @ Post-`wlim`):**
* **Continuous Power Cap:** **$1.0\text{ W}$ ($1,000\text{ mW}$)** per channel ($16\%$ of max $6.25\text{ W}$ peak power).
* **Linear RMS Threshold (`al`):** `0.4000` ($-7.96\text{ dBFS}$ RMS).
* **Heating Tau ($\tau_{\text{heat}}$):** $2,500\text{ ms}$ (2.5 seconds).
* **Cooling Tau ($\tau_{\text{cool}}$):** $5,000\text{ ms}$ (5.0 seconds).
* **Tweeter Thermal Guard (`tweeter_thermal_guard` @ Pre-`tlim`):**
* **Continuous Power Cap:** **$100\text{ mW}$ ($0.100\text{ W}$)** per channel ($1.6\%$ of max $6.25\text{ W}$ peak power).
* **Linear RMS Threshold (`al`):** `0.126491` ($-17.96\text{ dBFS}$ RMS).
* **Heating Tau ($\tau_{\text{heat}}$):** $1,000\text{ ms}$ (1.0 second).
* **Cooling Tau ($\tau_{\text{cool}}$):** $2,000\text{ ms}$ (2.0 seconds, fast decay recovery from Zoom/WebRTC glitches).
> **Acoustic Result:** Dynamic music (Blues, Classical, Rock) has high crest factor ($12\text{ dB} - 18\text{ dB}$) and operates at $0\text{ dB}$ gain reduction with full peak dynamics. Continuous high-power signals (full-volume white noise or sine sweeps) are automatically attenuated to harmless power levels after ~2 seconds.
---
## 🚀 Quick Start
### 1. Install Dependencies
@@ -118,7 +145,7 @@ Customize tone settings without touching calibrated internal DSP nodes. `user_eq
| **Electronic / Hip-Hop** | `1.26` | `1.19` | `1.00` | `0.94` | `1.00` | `1.00` | `1.06` | `1.00` |
| **Movie (Dialogue Focus)**| `0.84` | `0.94` | `1.00` | `1.06` | `1.19` | `1.19` | `1.06` | `1.00` |
| **Movie (Action / Bass)** | `1.41` | `1.12` | `1.00` | `1.00` | `1.00` | `1.06` | `1.12` | `1.12` |
| **Late-Night (Low Level)** | `0.63` | `0.79` | `1.00` | `1.00` | `1.06` | `1.12` | `1.00` | `0.94` |
| **Late-Night (Low Level)** | `0.63` | `0.79` | `1.00` | `1.00` | `1.06` | `1.19` | `1.00` | `0.94` |
*(Note: Gain values are linear multipliers: `1.0` = 0 dB, `1.41` ≈ +3 dB boost, `0.71` ≈ -3 dB cut)*
@@ -128,12 +155,28 @@ Customize tone settings without touching calibrated internal DSP nodes. `user_eq
If your specific physical unit requires custom acoustic tuning:
* **Woofer Ceiling:** Edit `wlim.control.limit` in `graph.json` (Default: `-2` dB. Lower to `-3` / `-4` dB for stricter mechanical protection).
* **Bass Drive:** Adjust `equalizer.control` (`g_1` through `g_5`).
* **Woofer Ceiling:** Edit `wlim.control.limit` in `graph.json` (Default: `0` dBFS into `thermal_guard`).
* **Thermal Recalibration:** Edit `al` in `thermal_guard` (`al = sqrt(P_watts / 6.25W)`).
* **Sub-Bass Synthesis:** Adjust `virtualbass.control.amt` (Default: `1.0`).
---
## ⚠️ AT-YOUR-OWN-RISK DISCLAIMER & Replacement Resources
> [!CAUTION]
> **USE AT YOUR OWN RISK:** Loud signals in general, as well as all continuous loud audio (e.g. uncompressed music, white/pink noise, full-volume sine sweeps, or software glitches), carry an inherent physical risk of voice-coil overheating, mechanical over-excursion, or permanent speaker driver damage.
>
> While this DSP engine includes virtual thermal-emulation limiters and dynamic excursion limiters as a **best-effort protective measure**, this software is provided **"AS IS", WITH NO IMPLIED WARRANTY OR GUARANTEE OF ANY KIND**. You are solely responsible for protecting your own hardware and managing playback volume levels. The authors and maintainers assume no responsibility or liability for damaged speaker cones, blown voice coils, or hardware failures.
### 🔧 Speaker Replacement & Repair Resources for Power Users
For expert users who wish to customize thresholds, push thermal limits higher will run a risk of speaker blowout.
Dont increase the limits unless you understand and are prepared for a speaker-driver replacement process at your own risk.
If you wish to replace speaker drivers or to be able to do so yourself these instructions and part-links may help.
* 🛠️ **iFixit Repair Guide:** [MacBook Pro 15" Touch Bar 2018 Speaker Replacement - iFixit](https://www.ifixit.com/Guide/MacBook+Pro+15-Inch+Touch+Bar+2018+Right+Speaker+Replacement/122784)
* 🛒 **Replacement Speaker Modules:** Cheap OEM speaker assemblies are readily available if you ever push limits and need a fresh set of drivers: search for MacBook Pro 15,1 / 15,2 (A1990 2018/2019) Left & Right Speaker Assemblies on [iFixit Parts](https://www.ifixit.com/Parts/MacBook_Pro_15%22_Touch_Bar) or [eBay A1990 Speakers](https://www.ebay.com/sch/i.html?_nkw=macbook+pro+a1990+speakers).
---
## 📚 Documentation & Repository Links
### 🔗 Repositories & Mirrors

View File

@@ -19,12 +19,11 @@ MERGED="$HOME/.audiograph.json"
GRAPH_DST="/usr/share/t2-linux-audio/15_1/graph.json"
FORCE=0
SIMPLE=1
SIMPLE=0
HOT=0
for arg in "${@:-}"; do
case "$arg" in
-f|--force|--no-check) FORCE=1 ;;
--no-bake) SIMPLE=0 ;;
-b|--bake|--simple) SIMPLE=1 ;;
-h|--hot|--soft) HOT=1 ;;
esac
@@ -33,6 +32,14 @@ done
have() { command -v "$1" >/dev/null 2>&1; }
die() { echo "Error: $*" >&2; exit 1; }
if [ "$SIMPLE" -eq 1 ]; then
echo "==> Baking static DSP stages into single-stage FIR files..."
python3 "$SCRIPT_DIR/bake-graph.py" || die "bake-graph.py failed"
GRAPH_SRC="$SCRIPT_DIR/graph_simple.json"
echo "==> Installing baked FIR files -> /usr/share/t2-linux-audio/15_1/"
sudo cp "$SCRIPT_DIR/15_1/baked-"*.wav "/usr/share/t2-linux-audio/15_1/" || die "Failed to copy baked FIR files"
fi
# --- json validation helper -------------------------------------------
json_ok() {
have python3 && { python3 -c 'import json, sys; json.load(open(sys.argv[1]))' "$1" 2>/dev/null && return 0; return 1; }
@@ -45,34 +52,24 @@ json_ok "$GRAPH_SRC"; rc=$?
[ "$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 ------------------------------------------
MERGED_SRC="$SCRIPT_DIR/graph_merged.json"
if [ -f "$OVERRIDE" ]; then
# --- build the effective graph -> ~/.audiograph.json --------------------
if [ "$SIMPLE" -eq 1 ]; then
cp "$GRAPH_SRC" "$MERGED"
echo "ok: baked single-stage graph_simple.json -> $MERGED"
elif [ -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_SRC 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_SRC" || die "jq merge failed"
echo "ok: merged user_eq.json -> $MERGED_SRC"
"$GRAPH_SRC" > "$MERGED" || die "jq merge failed"
echo "ok: merged user_eq.json -> $MERGED"
else
cp "$GRAPH_SRC" "$MERGED_SRC"
echo "ok: no user_eq.json - graph as-is -> $MERGED_SRC"
cp "$GRAPH_SRC" "$MERGED"
echo "ok: no user_eq.json - graph as-is -> $MERGED"
fi
if [ "$SIMPLE" -eq 1 ]; then
echo "==> Baking static DSP stages into single-stage FIR files..."
python3 "$SCRIPT_DIR/bake-graph.py" "$MERGED_SRC" || die "bake-graph.py failed"
GRAPH_SRC="$SCRIPT_DIR/graph_simple.json"
echo "==> Installing baked FIR files -> /usr/share/t2-linux-audio/15_1/"
sudo cp -f "$SCRIPT_DIR/laptop-configs/apple/mbp15_1/baked-"*.wav "/usr/share/t2-linux-audio/15_1/" 2>/dev/null || true
else
GRAPH_SRC="$MERGED_SRC"
fi
cp "$GRAPH_SRC" "$MERGED"
json_ok "$MERGED"; rc=$?
[ "$rc" -eq 1 ] && die "merged graph $MERGED is not valid JSON"
@@ -120,12 +117,10 @@ fi
# --- install ----------------------------------------------------------
if [ "$SIMPLE" -eq 1 ]; then
echo "Installing baked FIR files -> $(dirname "$GRAPH_DST")/"
for f in "$SCRIPT_DIR/laptop-configs/apple/mbp15_1/baked-"*.wav "$SCRIPT_DIR/15_1/baked-"*.wav "$SCRIPT_DIR/baked-"*.wav; do
if [ -f "$f" ]; then
bn="$(basename "$f")"
sudo cp "$f" "$(dirname "$GRAPH_DST")/$bn.tmp"
sudo mv -f "$(dirname "$GRAPH_DST")/$bn.tmp" "$(dirname "$GRAPH_DST")/$bn"
fi
for f in "$SCRIPT_DIR/15_1/baked-"*.wav; do
bn="$(basename "$f")"
sudo cp "$f" "$(dirname "$GRAPH_DST")/$bn.tmp"
sudo mv -f "$(dirname "$GRAPH_DST")/$bn.tmp" "$(dirname "$GRAPH_DST")/$bn"
done
fi

View File

@@ -92,72 +92,76 @@ def biquad_highshelf(fs, f0, gain_db, q=0.7071):
a2 = (A + 1.0) - (A - 1.0) * cos_w0 - beta * math.sin(w0)
return b0/a0, b1/a0, b2/a0, 1.0, a1/a0, a2/a0
def apply_biquad_to_samples(b0, b1, b2, a0, a1, a2, samples):
def process_biquad(samples, b0, b1, b2, a0, a1, a2):
out = [0.0] * len(samples)
x1 = x2 = y1 = y2 = 0.0
for i, x in enumerate(samples):
y = b0 * x + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2
for i in range(len(samples)):
x0 = samples[i]
y0 = b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2
out[i] = y0
x2 = x1
x1 = x
x1 = x0
y2 = y1
y1 = y
out[i] = y
y1 = y0
return out
def read_wav_floats(filepath):
with open(filepath, 'rb') as f:
data = f.read()
content = f.read()
if data[:4] != b'RIFF' or data[8:12] != b'WAVE':
raise ValueError(f"Not a valid RIFF WAVE file: {filepath}")
if not content.startswith(b'RIFF') or b'WAVE' not in content[:16]:
raise ValueError(f"Invalid WAV file: {filepath}")
# Parse RIFF chunks
pos = 12
fmt_found = False
audio_format = 1
fmt_tag = 1
nchannels = 1
fs = 48000
sampwidth = 2
raw_bytes = b''
framerate = 48000
sampwidth = 4
pcm_data = b''
while pos < len(content) - 8:
chunk_id = content[pos:pos+4]
chunk_size = struct.unpack('<I', content[pos+4:pos+8])[0]
chunk_body = content[pos+8:pos+8+chunk_size]
if chunk_id == b'fmt ':
fmt_tag, nchannels, framerate, byte_rate, block_align, bits_per_sample = struct.unpack('<HHIIHH', chunk_body[:16])
sampwidth = bits_per_sample // 8
elif chunk_id == b'data':
pcm_data = chunk_body
break
while pos + 8 <= len(data):
chunk_id = data[pos:pos+4]
chunk_size = struct.unpack('<I', data[pos+4:pos+8])[0]
chunk_data = data[pos+8:pos+8+chunk_size]
pos += 8 + chunk_size
if chunk_size % 2 == 1:
pos += 1
if chunk_id == b'fmt ':
audio_format, nchannels, fs, _, _, bits_per_sample = struct.unpack('<HHIIHH', chunk_data[:16])
sampwidth = bits_per_sample // 8
fmt_found = True
elif chunk_id == b'data':
raw_bytes = chunk_data
nframes = len(pcm_data) // (sampwidth * nchannels)
samples = []
if not fmt_found:
raise ValueError(f"No fmt chunk found in {filepath}")
n_samples = len(raw_bytes) // sampwidth
if sampwidth == 2:
ints = struct.unpack(f"<{n_samples}h", raw_bytes)
floats = [i / 32768.0 for i in ints]
elif sampwidth == 4:
floats = list(struct.unpack(f"<{n_samples}f", raw_bytes))
if fmt_tag == 3 and sampwidth == 4: # IEEE Float 32-bit
samples = list(struct.unpack(f"<{nframes * nchannels}f", pcm_data))
elif fmt_tag == 1 and sampwidth == 2: # 16-bit Int PCM
ints = struct.unpack(f"<{nframes * nchannels}h", pcm_data)
samples = [i / 32768.0 for i in ints]
elif fmt_tag == 1 and sampwidth == 4: # 32-bit Int PCM
ints = struct.unpack(f"<{nframes * nchannels}i", pcm_data)
samples = [i / 2147483648.0 for i in ints]
else:
raise ValueError(f"Unsupported sample width: {sampwidth}")
# Fallback 32-bit float unpack
samples = list(struct.unpack(f"<{nframes * nchannels}f", pcm_data))
if nchannels > 1:
mono_floats = [floats[i] for i in range(0, len(floats), nchannels)]
return mono_floats, fs
return floats, fs
samples = samples[::nchannels]
return samples, framerate
def write_wav_floats(filepath, samples, fs):
def write_wav_floats(filepath, samples, framerate):
data = struct.pack(f"<{len(samples)}f", *samples)
fmt_chunk = struct.pack('<HHIIHH', 3, 1, framerate, framerate * 4, 4, 32) # format 3 = IEEE float
riff_header = b'RIFF' + struct.pack('<I', 36 + len(data)) + b'WAVE'
fmt_header = b'fmt ' + struct.pack('<I', 16) + struct.pack('<HHIIHH', 3, 1, fs, fs * 4, 4, 32)
fmt_header = b'fmt ' + struct.pack('<I', 16) + fmt_chunk
data_header = b'data' + struct.pack('<I', len(data))
os.makedirs(os.path.dirname(os.path.abspath(filepath)), exist_ok=True)
with open(filepath, 'wb') as f:
f.write(riff_header + fmt_header + data_header + data)
@@ -222,134 +226,25 @@ def optimize_fir_latency_and_tail(samples, fs=48000, is_woofer=False):
return cropped
import cmath
def apply_pink_noise_target_filter(samples, fs=48000, f_ref=1000.0):
"""
Applies a continuous, exact 1/sqrt(f) (-3.01 dB/octave) Pink Noise target weighting filter
to convert raw white-noise sweep measurements to a true equal-energy-per-octave acoustic output.
"""
N = len(samples)
# Pure Python Cooley-Tukey FFT
def fft(x):
n = len(x)
if n <= 1:
return x
even = fft(x[0::2])
odd = fft(x[1::2])
terms = [cmath.exp(-2j * math.pi * k / n) * odd[k] for k in range(n // 2)]
return [even[k] + terms[k] for k in range(n // 2)] + [even[k] - terms[k] for k in range(n // 2)]
def ifft(x):
n = len(x)
if n <= 1:
return x
even = ifft(x[0::2])
odd = ifft(x[1::2])
terms = [cmath.exp(2j * math.pi * k / n) * odd[k] for k in range(n // 2)]
return [even[k] + terms[k] for k in range(n // 2)] + [even[k] - terms[k] for k in range(n // 2)]
# Pad N to next power of 2
pad_len = 1 << (N - 1).bit_length()
x_padded = [complex(s, 0.0) for s in samples] + [0j] * (pad_len - N)
fft_vals = fft(x_padded)
# Apply 1/sqrt(f) weighting
max_boost_lin = 10.0 ** (15.0 / 20.0) # +15 dB cap at subsonic
for k in range(pad_len):
freq = (k * fs) / pad_len
if k > pad_len // 2:
freq = ((pad_len - k) * fs) / pad_len
if freq > 10.0:
weight = math.sqrt(f_ref / max(freq, 10.0))
weight = min(weight, max_boost_lin)
else:
weight = 1.0
fft_vals[k] *= weight
ifft_vals = ifft(fft_vals)
pink_samples = [ifft_vals[k].real / pad_len for k in range(N)]
return pink_samples
def extract_biquads_from_node(node, fs):
biquads = []
control = node.get("control", {})
if control.get("enabled", 1) == 0:
return biquads
for i in range(16):
f_key = f"f_{i}"
g_key = f"g_{i}"
q_key = f"q_{i}"
ft_key = f"ft_{i}"
if f_key not in control:
break
f = control[f_key]
g_lin = control.get(g_key, 1.0)
q = control.get(q_key, 0.7071)
ft = control.get(ft_key, 1)
if g_lin <= 0.0001:
gain_db = -80.0
else:
gain_db = 20.0 * math.log10(g_lin)
if abs(gain_db) < 0.001 or ft == 0:
continue
if ft == 1:
coeffs = biquad_peaking(fs, f, gain_db, q)
elif ft == 3:
coeffs = biquad_highshelf(fs, f, gain_db, q)
elif ft == 5:
coeffs = biquad_lowshelf(fs, f, gain_db, q)
else:
continue
biquads.append(coeffs)
return biquads
def bake_driver_ir(src_wav, dst_wav, is_woofer=False, driver_gain=1.0, biquads=[]):
def bake_driver_ir(src_wav, dst_wav, is_woofer=False, driver_gain=1.0):
if not os.path.exists(src_wav):
print(f"Warning: {src_wav} not found, skipping.")
return False
samples, fs = read_wav_floats(src_wav)
# 1. Convolve all static LTI biquad stages (User EQ + Voicing EQ) directly into driver IR
for coeffs in biquads:
b0, b1, b2, a0, a1, a2 = coeffs
samples = apply_biquad_to_samples(b0, b1, b2, a0, a1, a2, samples)
# 2. Optimize Latency (5ms Lead) + Extend Woofer/Tweeter Lopsided Tail Resolution
# 1. Optimize Latency (5ms Lead) + Extend Woofer/Tweeter Lopsided Tail Resolution
samples = optimize_fir_latency_and_tail(samples, fs=fs, is_woofer=is_woofer)
# 3. True-Peak Inter-Sample Peak (ISP) Guarding (-0.5 dBFS ceiling)
samples = apply_true_peak_guard(samples, max_allowed_dbfs=-0.5)
write_wav_floats(dst_wav, samples, fs)
print(f"==> Baked {os.path.basename(dst_wav)} ({fs} Hz, {len(samples)} taps, {len(biquads)} biquads convolved)")
print(f"==> Baked {os.path.basename(dst_wav)} ({fs} Hz, {len(samples)} taps, gain={driver_gain}x)")
return True
def generate_simple_graph_and_bake(profile_dir=None, input_graph_path=None):
if not profile_dir:
try:
import detect_hardware
profile_dir, prof = detect_hardware.detect_profile()
except Exception:
profile_dir = os.path.join(SCRIPT_DIR, "laptop-configs", "apple", "mbp15_1")
if not profile_dir or not os.path.exists(profile_dir):
profile_dir = os.path.join(SCRIPT_DIR, "15_1")
if input_graph_path and os.path.exists(input_graph_path):
graph_path = input_graph_path
else:
graph_path = os.path.join(profile_dir, "graph.json")
if not os.path.exists(graph_path):
graph_path = os.path.join(SCRIPT_DIR, "graph.json")
def generate_simple_graph_and_bake():
graph_path = os.path.join(SCRIPT_DIR, "graph.json")
simple_graph_path = os.path.join(SCRIPT_DIR, "graph_simple.json")
if not os.path.exists(graph_path):
@@ -359,14 +254,14 @@ def generate_simple_graph_and_bake(profile_dir=None, input_graph_path=None):
with open(graph_path, 'r') as f:
graph = json.load(f)
repo_151 = profile_dir
repo_151 = os.path.join(SCRIPT_DIR, "15_1")
sys_dir = "/usr/share/t2-linux-audio/15_1"
os.makedirs(repo_151, exist_ok=True)
nodes = graph.get("filter.graph", {}).get("nodes", [])
# 1. Discover all convolver nodes and their input WAV files dynamically from graph.json
convolver_tasks = {} # maps raw_basename -> task dict
convolver_tasks = {} # maps src_filename -> {is_woofer, gain, sys_dst_path, repo_dst_path}
for node in nodes:
if node.get("label") == "convolver" or "conv" in node.get("name", ""):
@@ -377,126 +272,103 @@ def generate_simple_graph_and_bake(profile_dir=None, input_graph_path=None):
is_woofer = ("woofer" in name.lower() or "convlw" in name.lower() or "convrw" in name.lower())
for sys_path in filenames:
raw_basename = os.path.basename(sys_path).replace("baked-", "")
if not raw_basename in convolver_tasks:
if "woofer" in raw_basename.lower():
basename = os.path.basename(sys_path)
if not basename in convolver_tasks:
if "woofer" in basename.lower():
is_woofer = True
baked_basename = "baked-" + raw_basename
baked_basename = "baked-" + basename
repo_dst_path = os.path.join(repo_151, baked_basename)
sys_dst_path = os.path.join(sys_dir, baked_basename)
convolver_tasks[raw_basename] = {
"raw_basename": raw_basename,
"baked_basename": baked_basename,
convolver_tasks[sys_path] = {
"basename": basename,
"is_woofer": is_woofer,
"gain": gain,
"repo_dst": repo_dst_path,
"sys_dst": sys_dst_path
}
# 2. Discover all static biquad EQ nodes (user_eq and equalizer) to convolve into FIR targets
biquad_nodes = [node for node in nodes if node.get("name") in ["user_eq", "equalizer"]]
# 3. Bake FIR files dynamically for all discovered WAV targets from PURE raw measurement source files
for raw_basename, task in convolver_tasks.items():
src_path = os.path.join(repo_151, raw_basename)
if not os.path.exists(src_path):
src_path = os.path.join(SCRIPT_DIR, "15_1", raw_basename)
if not os.path.exists(src_path):
src_path = os.path.join(SCRIPT_DIR, raw_basename)
if not os.path.exists(src_path):
src_path = os.path.join(sys_dir, raw_basename)
if not os.path.exists(src_path):
print(f"Warning: Raw measurement source {raw_basename} not found in {repo_151} or {sys_dir}.")
continue
# Read src_path to determine sample rate fs for exact biquad coefficient generation
_, fs = read_wav_floats(src_path)
biquads = []
for bnode in biquad_nodes:
biquads.extend(extract_biquads_from_node(bnode, fs))
# 2. Bake FIR files dynamically for all discovered WAV targets
for sys_path, task in convolver_tasks.items():
basename = task["basename"]
src_path = os.path.join(repo_151, basename)
if not os.path.exists(src_path) and os.path.exists(sys_path):
src_path = sys_path
if not os.path.exists(src_path) and os.path.exists(os.path.join(SCRIPT_DIR, basename)):
src_path = os.path.join(SCRIPT_DIR, basename)
bake_driver_ir(
src_wav=src_path,
dst_wav=task["repo_dst"],
is_woofer=task["is_woofer"],
driver_gain=task["gain"],
biquads=biquads
driver_gain=task["gain"]
)
# 3. Build graph_simple.json dynamically from graph.json (omitting user_eq, equalizer, whp* nodes, and disabled loudness)
# 3. Build graph_simple.json dynamically from graph.json (omitting limiter, ell, elr, whp*)
# Keeping user_eq, equalizer, virtualbass, multiband_compressor in exact order for 100% bit-exact bass response!
graph["node.description"] = "MacBook Pro 15,1 DSP Speakers (Baked FIR Crossovers & Latency Trimming)"
new_nodes = []
loudness_node = next((n for n in nodes if n.get("name") == "loudness"), None)
loudness_enabled = loudness_node and loudness_node.get("control", {}).get("enabled", 1) == 1
for node in nodes:
name = node.get("name", "")
# Omit user_eq, equalizer (baked into FIRs), whp* crossover nodes, and disabled loudness
if name in ["user_eq", "equalizer", "whpL1", "whpL2", "whpR1", "whpR2"]:
continue
if name == "loudness" and not loudness_enabled:
# Omit redundant master limiter, ell/elr mono nodes, & crossover biquad nodes
if name in ["limiter", "ell", "elr", "whpL1", "whpL2", "whpR1", "whpR2"]:
continue
if node.get("label") == "convolver" or "conv" in name:
orig_filenames = node.get("config", {}).get("filename", [])
baked_fns = []
for p in orig_filenames:
rb = os.path.basename(p).replace("baked-", "")
if rb in convolver_tasks:
baked_fns.append(convolver_tasks[rb]["sys_dst"])
else:
baked_fns.append(os.path.join(sys_dir, "baked-" + rb))
node["config"]["filename"] = baked_fns
node["config"]["filename"] = [
convolver_tasks[p]["sys_dst"] if p in convolver_tasks else os.path.join(sys_dir, "baked-" + os.path.basename(p))
for p in orig_filenames
]
new_nodes.append(node)
# Re-wire links: filter out removed user_eq, equalizer & whp* crossover links
# Add consolidated 2-channel stereo loudness compensator node (replacing ell & elr)
new_nodes.append({
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/loud_comp_stereo",
"name": "loudness",
"control": {
"enabled": 1,
"input": 1.0,
"fft": 4
}
})
# Re-wire links: filter out limiter, ell, elr & whp*
links = graph.get("filter.graph", {}).get("links", [])
new_links = []
for link in links:
out_node = link.get("output", "")
in_node = link.get("input", "")
if ("user_eq" in out_node or "user_eq" in in_node or
"equalizer" in out_node or "equalizer" in in_node or
"whp" in out_node or "whp" in in_node or
(not loudness_enabled and ("loudness" in out_node or "loudness" in in_node))):
if ("whp" in out_node or "whp" in in_node or
"limiter:" in out_node or "limiter:" in in_node or
"ell:" in out_node or "ell:" in in_node or
"elr:" in out_node or "elr:" in in_node):
continue
new_links.append(link)
# Wire virtualbass output
if loudness_enabled:
new_links.append({"output": "virtualbass:out_l", "input": "loudness:in_l"})
new_links.append({"output": "virtualbass:out_r", "input": "loudness:in_r"})
else:
new_links.append({"output": "virtualbass:out_l", "input": "copyL:In"})
new_links.append({"output": "virtualbass:out_r", "input": "copyR:In"})
# Wire multiband_compressor -> loudness (stereo) -> copyL / copyR
new_links.append({"output": "multiband_compressor:out_l", "input": "loudness:in_l"})
new_links.append({"output": "multiband_compressor:out_r", "input": "loudness:in_r"})
new_links.append({"output": "loudness:out_l", "input": "copyL:In"})
new_links.append({"output": "loudness:out_r", "input": "copyR:In"})
# Set graph inputs directly to virtualbass (first active node in simplified processing chain)
# Set graph inputs directly to user_eq (first node in processing chain)
graph["filter.graph"]["inputs"] = [
"virtualbass:in_l",
"virtualbass:in_r"
"user_eq:in_l",
"user_eq:in_r"
]
# Remove capture.volumes from filter.graph if present
graph["filter.graph"].pop("capture.volumes", None)
# Consolidated volume tracking inside capture.props if loudness enabled
if "capture.props" not in graph:
graph["capture.props"] = {}
if loudness_enabled:
graph["capture.props"]["capture.volumes"] = [
{
"control": "loudness:volume",
"min": -65.0,
"max": 0.0,
"scale": "cubic"
}
]
else:
graph["capture.props"].pop("capture.volumes", None)
# Consolidated volume tracking for stereo loudness node
graph["filter.graph"]["capture.volumes"] = [
{
"control": "loudness:volume",
"min": -65.0,
"max": 0.0,
"scale": "cubic"
}
]
graph["filter.graph"]["nodes"] = new_nodes
graph["filter.graph"]["links"] = new_links
@@ -510,8 +382,7 @@ def main():
print("=================================================================")
print(" SINGLE-STAGE FIR CONVOLVER BAKER & GRAPH SIMPLIFIER")
print("=================================================================")
input_graph = sys.argv[1] if len(sys.argv) > 1 else None
generate_simple_graph_and_bake(input_graph_path=input_graph)
generate_simple_graph_and_bake()
print("=================================================================")
print("Done! Baked FIR files & graph_simple.json created.")

View File

@@ -1,150 +0,0 @@
#!/usr/bin/env python3
import os
import sys
import json
import glob
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
CONFIGS_DIR = os.path.join(SCRIPT_DIR, "laptop-configs")
def print_help():
print("""=================================================================
LAPTOP AUDIO DSP HARDWARE DETECTION TOOL
=================================================================
Usage:
./detect_hardware.py [options]
Options:
-h, --help Show this help message
-l, --list-configs List all available laptop hardware profiles
-m, --model <name> Manually specify model/profile ID (e.g., apple_mbp15_1)
--manual <name> Same as --model
-p, --profile <path> Path to custom profile directory
--json Output detection details in JSON format
--quiet Suppress warning messages on fallback
Examples:
./detect_hardware.py
./detect_hardware.py --list-configs
./detect_hardware.py --model apple_mbp15_1
./detect_hardware.py --json
=================================================================
""")
def read_dmi_file(filename):
path = os.path.join("/sys/class/dmi/id", filename)
if os.path.exists(path):
try:
with open(path, 'r') as f:
return f.read().strip()
except Exception:
pass
return ""
def list_all_profiles():
profile_files = glob.glob(os.path.join(CONFIGS_DIR, "**", "profile.json"), recursive=True)
profiles = []
for prof_path in sorted(profile_files):
try:
with open(prof_path, 'r') as f:
prof = json.load(f)
prof["_path"] = os.path.dirname(prof_path)
profiles.append(prof)
except Exception:
continue
return profiles
def print_available_configs():
profiles = list_all_profiles()
print("=================================================================")
print(" AVAILABLE LAPTOP AUDIO DSP CONFIGURATIONS")
print("=================================================================")
print(f" {'ID':<18} | {'Name':<32} | {'Vendor':<12}")
print("-------------------+----------------------------------+----------")
for prof in profiles:
pid = prof.get("id", os.path.basename(prof["_path"]))
name = prof.get("name", "Unknown Laptop")
vendor = prof.get("vendor", "Unknown")
print(f" {pid:<18} | {name:<32} | {vendor:<12}")
print("=================================================================")
def detect_profile(manual_target=None):
profile_files = glob.glob(os.path.join(CONFIGS_DIR, "**", "profile.json"), recursive=True)
# 1. Manual override check (--model, --manual, --profile)
if manual_target:
for prof_path in profile_files:
try:
with open(prof_path, 'r') as f:
prof = json.load(f)
pid = prof.get("id", os.path.basename(os.path.dirname(prof_path)))
pdir = os.path.dirname(prof_path)
if manual_target.lower() in [pid.lower(), pdir.lower(), os.path.basename(pdir).lower()]:
return pdir, prof, False
except Exception:
continue
# 2. DMI Hardware Matching
vendor = read_dmi_file("sys_vendor")
product = read_dmi_file("product_name")
if vendor or product:
for prof_path in profile_files:
try:
with open(prof_path, 'r') as f:
prof = json.load(f)
dmi_matches = prof.get("dmi_matches", [])
prof_vendor = prof.get("vendor", "")
if (not prof_vendor or prof_vendor.lower() in vendor.lower() or vendor.lower() in prof_vendor.lower()):
for match in dmi_matches:
if match.lower() in product.lower():
return os.path.dirname(prof_path), prof, False
except Exception:
continue
# 3. Fallback to default MacBookPro15,1 profile with warning
fallback_path = os.path.join(CONFIGS_DIR, "apple", "mbp15_1")
fallback_json = os.path.join(fallback_path, "profile.json")
if os.path.exists(fallback_json):
with open(fallback_json, 'r') as f:
return fallback_path, json.load(f), True
return None, None, True
def main():
args = sys.argv[1:]
if any(arg in args for arg in ["-h", "--help", "help"]):
print_help()
sys.exit(0)
if any(arg in args for arg in ["--list", "--list-configs", "-l", "list"]):
print_available_configs()
sys.exit(0)
manual_target = None
for i, arg in enumerate(args):
if arg in ["--profile", "-p", "--model", "-m", "--manual"] and i + 1 < len(args):
manual_target = args[i+1]
path, prof, is_fallback = detect_profile(manual_target=manual_target)
if is_fallback and "--quiet" not in args:
vendor = read_dmi_file("sys_vendor") or "Unknown Vendor"
product = read_dmi_file("product_name") or "Unknown Model"
print(f"Warning: Hardware '{vendor} / {product}' not found in laptop-configs/.", file=sys.stderr)
print("Falling back to default 'apple_mbp15_1' profile.", file=sys.stderr)
print("Use './detect_hardware.py --list-configs' to view all available profiles.", file=sys.stderr)
if "--json" in args:
print(json.dumps({"path": path, "profile": prof, "is_fallback": is_fallback}, indent=2))
else:
if path:
print(path)
else:
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -1,6 +1,6 @@
{
"node.description": "MacBook Pro 15,1 Audio DSP",
"media.name": "MacBook Pro 15,1 Audio DSP",
"node.description": "MacBook Pro 15,1 DSP Speakers",
"media.name": "MacBook Pro 15,1 DSP Speakers",
"filter.graph": {
"nodes": [
{
@@ -8,15 +8,43 @@
"plugin": "http://lsp-plug.in/plugins/lv2/para_equalizer_x16_stereo",
"name": "user_eq",
"control": {
"enabled": 1, "mode": 0, "g_in": 1.0, "g_out": 1.5,
"ft_0": 5, "f_0": 70.0, "g_0": 1.00, "q_0": 0.70, "s_0": 0,
"ft_1": 1, "f_1": 110.0, "g_1": 1.00, "q_1": 1.00,
"ft_2": 1, "f_2": 315.0, "g_2": 1.00, "q_2": 1.00,
"ft_3": 1, "f_3": 600.0, "g_3": 1.00, "q_3": 1.00,
"ft_4": 1, "f_4": 1000.0, "g_4": 1.00, "q_4": 1.00,
"ft_5": 1, "f_5": 2500.0, "g_5": 1.00, "q_5": 1.00,
"ft_6": 1, "f_6": 6000.0, "g_6": 1.00, "q_6": 1.00,
"ft_7": 3, "f_7": 12000.0,"g_7": 1.00, "q_7": 0.70, "s_7": 0
"enabled": 0, "mode": 0, "g_in": 1.0, "g_out": 1.00,
"ft_0": 5, "f_0": 70.0 , "g_0": 1.1, "q_0": 0.7, "s_0": 0,
"ft_1": 1, "f_1": 110.0 , "g_1": 1.1, "q_1": 1.0,
"ft_2": 1, "f_2": 315.0 , "g_2": 1.05, "q_2": 1.0,
"ft_3": 1, "f_3": 1000.0 , "g_3": 1.01, "q_3": 1.0,
"ft_4": 1, "f_4": 2500.0 , "g_4": 0.98, "q_4": 1.0,
"ft_5": 1, "f_5": 6000.0 , "g_5": 0.97, "q_5": 1.0,
"ft_6": 3, "f_6": 10000.0, "g_6": 0.96, "q_6": 0.7,
"ft_7": 3, "f_7": 16000.0, "g_7": 0.95, "q_7": 0.7, "s_7": 0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/para_equalizer_x16_stereo",
"name": "equalizer",
"control": {
"enabled": 1,
"mode": 0,
"g_in": 1.0,
"g_out": 1.2,
"ft_0": 2, "f_0": 20.0, "g_0": 2.5637, "q_0": 1.41, "s_0": 3, "xm_0": 0, "fm_0": 0,
"ft_1": 1, "f_1": 31.5, "g_1": 2.2448, "q_1": 1.41, "s_1": 0, "xm_1": 0, "fm_1": 0,
"ft_2": 1, "f_2": 50.0, "g_2": 1.9610, "q_2": 1.41, "s_2": 0, "xm_2": 0, "fm_2": 0,
"ft_3": 1, "f_3": 80.0, "g_3": 1.7091, "q_3": 1.41, "s_3": 0, "xm_3": 0, "fm_3": 0,
"ft_4": 1, "f_4": 125.0, "g_4": 1.5, "q_4": 1.41, "s_4": 0, "xm_4": 0, "fm_4": 0,
"ft_5": 1, "f_5": 200.0, "g_5": 1.3073, "q_5": 1.41, "s_5": 0, "xm_5": 0, "fm_5": 0,
"ft_6": 1, "f_6": 315.0, "g_6": 1.1447, "q_6": 1.41, "s_6": 0, "xm_6": 0, "fm_6": 0,
"ft_7": 1, "f_7": 500.0, "g_7": 1.0, "q_7": 1.41, "s_7": 0, "xm_7": 0, "fm_7": 0,
"ft_8": 1, "f_8": 800.0, "g_8": 0.8716, "q_8": 1.41, "s_8": 0, "xm_8": 0, "fm_8": 0,
"ft_9": 1, "f_9": 1250.0, "g_9": 0.7649, "q_9": 1.41, "s_9": 0, "xm_9": 0, "fm_9": 0,
"ft_10": 1, "f_10": 2000.0, "g_10": 0.6667, "q_10": 1.41, "s_10": 0, "xm_10": 0, "fm_10": 0,
"ft_11": 1, "f_11": 3150.0, "g_11": 0.5837, "q_11": 1.41, "s_11": 0, "xm_11": 0, "fm_11": 0,
"ft_12": 1, "f_12": 5000.0, "g_12": 0.5099, "q_12": 1.41, "s_12": 0, "xm_12": 0, "fm_12": 0,
"ft_13": 1, "f_13": 8000.0, "g_13": 0.4444, "q_13": 1.41, "s_13": 0, "xm_13": 0, "fm_13": 0,
"ft_14": 1, "f_14": 12500.0, "g_14": 0.3901, "q_14": 1.41, "s_14": 0, "xm_14": 0, "fm_14": 0,
"ft_15": 1, "f_15": 20000.0, "g_15": 0.3400, "q_15": 1.41, "s_15": 0, "xm_15": 0, "fm_15": 0
}
},
{
@@ -24,39 +52,59 @@
"plugin": "https://chadmed.au/bankstown",
"name": "virtualbass",
"control": {
"amt": 1.4,
"floor": 40.0,
"ceil": 120.0,
"final_hp": 55.0,
"bypass": 0,
"amt": 1.0,
"sat_second": 1.0,
"sat_third": 1.0,
"blend": 0.30
"blend": 1.0,
"ceil": 90.0,
"floor": 60.0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/para_equalizer_x16_stereo",
"name": "equalizer",
"control": {
"enabled": 1, "mode": 0, "g_in": 1.0, "g_out": 1.0,
"ft_0": 5, "f_0": 40.0, "g_0": 2.83, "q_0": 0.70,
"ft_1": 1, "f_1": 80.0, "g_1": 2.00, "q_1": 1.00,
"ft_2": 1, "f_2": 160.0, "g_2": 1.41, "q_2": 1.00,
"ft_3": 1, "f_3": 320.0, "g_3": 1.00, "q_3": 1.00,
"ft_4": 1, "f_4": 640.0, "g_4": 0.71, "q_4": 1.00,
"ft_5": 1, "f_5": 1280.0, "g_5": 0.50, "q_5": 1.00,
"ft_6": 1, "f_6": 2560.0, "g_6": 0.35, "q_6": 1.00,
"ft_7": 3, "f_7": 12000.0,"g_7": 0.24, "q_7": 0.70
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/loud_comp_stereo",
"name": "loudness",
"plugin": "http://lsp-plug.in/plugins/lv2/mb_compressor_stereo",
"name": "multiband_compressor",
"control": {
"enabled": 1,
"input": 1.0,
"fft": 1
"mode": 1,
"g_in": 1.0,
"g_out": 1.0,
"g_dry": 0.0001,
"g_wet": 1.0,
"cbe_1": 1, "sf_1": 40.0, "al_1": 0.10,
"cbe_2": 1, "sf_2": 50.0, "al_2": 0.3,
"cbe_3": 1, "sf_3": 60.0, "al_3": 0.5,
"cbe_4": 1, "sf_4": 80.0, "al_4": 0.7,
"cbe_5": 1, "sf_5": 120.0, "al_5": 0.7,
"cbe_6": 1, "sf_6": 200.0, "al_6": 0.7,
"cbe_7": 1, "sf_7": 500.0, "al_7": 0.7,
"ce_0": 1, "at_0": 5.0, "rt_0": 100.0, "cr_0": 30.0, "kn_0": 0.10, "al_0": 0.000, "mk_0": 1.0, "bth_0": 1.0, "bsa_0": 1.0, "scm_0": 0,
"ce_1": 1, "at_1": 6.0, "rt_1": 110.0, "cr_1": 30.0, "kn_1": 0.10, "mk_1": 1.0, "bth_1": 1.0, "bsa_1": 1.0, "scm_1": 0,
"ce_2": 1, "at_2": 6.0, "rt_2": 100.0, "cr_2": 25.0, "kn_2": 0.12, "mk_2": 1.1, "bth_2": 1.0, "bsa_2": 1.0, "scm_2": 0,
"ce_3": 1, "at_3": 5.0, "rt_3": 90.0, "cr_3": 20.0, "kn_3": 0.12, "mk_3": 1.2, "bth_3": 1.0, "bsa_3": 1.0, "scm_3": 0,
"ce_4": 1, "at_4": 4.5, "rt_4": 80.0, "cr_4": 16.0, "kn_4": 0.15, "mk_4": 1.3, "bth_4": 1.0, "bsa_4": 1.0, "scm_4": 0,
"ce_5": 1, "at_5": 4.0, "rt_5": 60.0, "cr_5": 10.0, "kn_5": 0.20, "mk_5": 1.5, "bth_5": 1.0, "bsa_5": 1.0, "scm_5": 0,
"ce_6": 1, "at_6": 3.0, "rt_6": 40.0, "cr_6": 6.0, "kn_6": 0.30, "mk_6": 1.4, "bth_6": 1.0, "bsa_6": 1.0, "scm_6": 0,
"ce_7": 1, "at_7": 2.5, "rt_7": 30.0, "cr_7": 4.0, "kn_7": 0.40, "mk_7": 1.2, "bth_7": 1.0, "bsa_7": 1.0, "scm_7": 0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/loud_comp_mono",
"name": "ell",
"control": {
"enabled": 1,
"volume": 0.0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/loud_comp_mono",
"name": "elr",
"control": {
"enabled": 1,
"volume": 0.0
}
},
{
@@ -80,7 +128,7 @@
"/usr/share/t2-linux-audio/15_1/tweeters-96k.wav"
],
"channel": 0,
"gain": 1.1
"gain": 1.2
}
},
{
@@ -94,7 +142,7 @@
"/usr/share/t2-linux-audio/15_1/tweeters-96k.wav"
],
"channel": 0,
"gain": 1.1
"gain": 1.2
}
},
{
@@ -127,50 +175,19 @@
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/mb_compressor_stereo",
"name": "wmb_comp",
"name": "thermal_guard",
"plugin": "http://lsp-plug.in/plugins/lv2/sc_compressor_stereo",
"control": {
"enabled": 1,
"mode": 1,
"g_in": 1.0,
"g_out": 1.1,
"g_dry": 0.0001,
"g_wet": 1.0,
"cbe_1": 1, "sf_1": 40.0,
"cbe_2": 1, "sf_2": 50.0,
"cbe_3": 1, "sf_3": 60.0,
"cbe_4": 1, "sf_4": 80.0,
"cbe_5": 1, "sf_5": 120.0,
"cbe_6": 1, "sf_6": 200.0,
"cbe_7": 1, "sf_7": 500.0,
"ce_0": 1, "at_0": 5.0, "rt_0": 100.0, "cr_0": 30.0, "kn_0": 0.10, "al_0": 0.95, "mk_0": 1.0, "bth_0": 1.0, "bsa_0": 1.0, "scm_0": 0,
"ce_1": 1, "at_1": 6.0, "rt_1": 110.0, "cr_1": 30.0, "kn_1": 0.10, "al_1": 0.92, "mk_1": 1.0, "bth_1": 1.0, "bsa_1": 1.0, "scm_1": 0,
"ce_2": 1, "at_2": 6.0, "rt_2": 100.0, "cr_2": 25.0, "kn_2": 0.12, "al_2": 0.90, "mk_2": 1.0, "bth_2": 1.0, "bsa_2": 1.0, "scm_2": 0,
"ce_3": 1, "at_3": 5.0, "rt_3": 90.0, "cr_3": 20.0, "kn_3": 0.12, "al_3": 0.88, "mk_3": 1.0, "bth_3": 1.0, "bsa_3": 1.0, "scm_3": 0,
"ce_4": 1, "at_4": 4.5, "rt_4": 80.0, "cr_4": 16.0, "kn_4": 0.15, "al_4": 0.85, "mk_4": 1.0, "bth_4": 1.0, "bsa_4": 1.0, "scm_4": 0,
"ce_5": 1, "at_5": 4.0, "rt_5": 60.0, "cr_5": 10.0, "kn_5": 0.20, "al_5": 0.80, "mk_5": 1.0, "bth_5": 1.0, "bsa_5": 1.0, "scm_5": 0,
"ce_6": 1, "at_6": 3.0, "rt_6": 40.0, "cr_6": 6.0, "kn_6": 0.30, "al_6": 0.80, "mk_6": 1.0, "bth_6": 1.0, "bsa_6": 1.0, "scm_6": 0,
"ce_7": 1, "at_7": 2.5, "rt_7": 30.0, "cr_7": 4.0, "kn_7": 0.40, "al_7": 0.80, "mk_7": 1.0, "bth_7": 1.0, "bsa_7": 1.0, "scm_7": 0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/mb_compressor_stereo",
"name": "tmb_comp",
"control": {
"enabled": 1,
"mode": 1,
"g_in": 1.0,
"g_out": 1.0,
"g_dry": 0.0001,
"g_wet": 1.0,
"cbe_1": 1, "sf_1": 2500.0,
"cbe_2": 1, "sf_2": 6000.0,
"cbe_3": 1, "sf_3": 12000.0,
"ce_0": 1, "at_0": 3.0, "rt_0": 50.0, "cr_0": 8.0, "kn_0": 0.20, "al_0": 0.85, "mk_0": 1.0, "bth_0": 1.0, "bsa_0": 1.0, "scm_0": 0,
"ce_1": 1, "at_1": 2.5, "rt_1": 40.0, "cr_1": 6.0, "kn_1": 0.30, "al_1": 0.80, "mk_1": 1.0, "bth_1": 1.0, "bsa_1": 1.0, "scm_1": 0,
"ce_2": 1, "at_2": 2.0, "rt_2": 30.0, "cr_2": 4.0, "kn_2": 0.40, "al_2": 0.75, "mk_2": 1.0, "bth_2": 1.0, "bsa_2": 1.0, "scm_2": 0,
"ce_3": 1, "at_3": 1.5, "rt_3": 20.0, "cr_3": 3.0, "kn_3": 0.50, "al_3": 0.70, "mk_3": 1.0, "bth_3": 1.0, "bsa_3": 1.0, "scm_3": 0
"at": 2500.0,
"rt": 5000.0,
"al": 0.5,
"cr": 4.0,
"kn": 0.5,
"scp": 0,
"scm": 1
}
},
{
@@ -179,7 +196,7 @@
"plugin": "http://plugin.org.uk/swh-plugins/fastLookaheadLimiter",
"control": {
"ingain": 0,
"limit": 0,
"limit": -0.5,
"release": 0.25
}
},
@@ -189,32 +206,32 @@
"plugin": "http://plugin.org.uk/swh-plugins/fastLookaheadLimiter",
"control": {
"ingain": 0,
"limit": -1,
"limit": -0.5,
"release": 0.15
}
}
],
"links": [
{"output": "user_eq:out_l", "input": "virtualbass:in_l"},
{"output": "user_eq:out_r", "input": "virtualbass:in_r"},
{"output": "virtualbass:out_l", "input": "equalizer:in_l"},
{"output": "virtualbass:out_r", "input": "equalizer:in_r"},
{"output": "equalizer:out_l", "input": "loudness:in_l"},
{"output": "equalizer:out_r", "input": "loudness:in_r"},
{"output": "loudness:out_l", "input": "copyL:In"},
{"output": "loudness:out_r", "input": "copyR:In"},
{"output": "user_eq:out_l", "input": "equalizer:in_l"},
{"output": "user_eq:out_r", "input": "equalizer:in_r"},
{"output": "equalizer:out_l", "input": "virtualbass:in_l"},
{"output": "equalizer:out_r", "input": "virtualbass:in_r"},
{"output": "virtualbass:out_l", "input": "thermal_guard:in_l"},
{"output": "virtualbass:out_r", "input": "thermal_guard:in_r"},
{"output": "thermal_guard:out_l", "input": "ell:in"},
{"output": "thermal_guard:out_r", "input": "elr:in"},
{"output": "ell:out", "input": "copyL:In"},
{"output": "elr:out", "input": "copyR:In"},
{"output": "copyL:Out", "input": "convLT:In"},
{"output": "copyL:Out", "input": "convLW:In"},
{"output": "copyR:Out", "input": "convRT:In"},
{"output": "copyR:Out", "input": "convRW:In"},
{"output": "convLW:Out", "input": "wmb_comp:in_l"},
{"output": "convRW:Out", "input": "wmb_comp:in_r"},
{"output": "wmb_comp:out_l", "input": "wlim:in_1"},
{"output": "wmb_comp:out_r", "input": "wlim:in_2"},
{"output": "convLT:Out", "input": "tmb_comp:in_l"},
{"output": "convRT:Out", "input": "tmb_comp:in_r"},
{"output": "tmb_comp:out_l", "input": "tlim:in_1"},
{"output": "tmb_comp:out_r", "input": "tlim:in_2"}
{"output": "convLW:Out", "input": "multiband_compressor:in_l"},
{"output": "convRW:Out", "input": "multiband_compressor:in_r"},
{"output": "multiband_compressor:out_l", "input": "wlim:in_1"},
{"output": "multiband_compressor:out_r", "input": "wlim:in_2"},
{"output": "convLT:Out", "input": "tlim:in_1"},
{"output": "convRT:Out", "input": "tlim:in_2"}
],
"inputs": [
"user_eq:in_l",
@@ -225,6 +242,20 @@
"wlim:out_2",
"tlim:out_1",
"tlim:out_2"
],
"capture.volumes": [
{
"control": "ell:volume",
"min": -65.0,
"max": 0.0,
"scale": "linear"
},
{
"control": "elr:volume",
"min": -65.0,
"max": 0.0,
"scale": "linear"
}
]
},
"capture.props": {

237
graph.simpl2.json Normal file
View File

@@ -0,0 +1,237 @@
{
"node.description": "MacBook Pro 15,1 DSP Speakers",
"media.name": "MacBook Pro 15,1 DSP Speakers",
"filter.graph": {
"nodes": [
{
"type": "lv2",
"plugin": "https://chadmed.au/bankstown",
"name": "virtualbass",
"control": {
"bypass": 0,
"amt": 1.0,
"sat_second": 1.0,
"sat_third": 1.0,
"blend": 1.0,
"ceil": 150.0,
"floor": 60.0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/mb_compressor_stereo",
"name": "multiband_compressor",
"control": {
"enabled": 1,
"mode": 1,
"g_in": 1.0,
"g_out": 1.0,
"g_dry": 0.0001,
"g_wet": 1.0,
"cbe_1": 1, "sf_1": 40.0, "al_1": 0.05,
"cbe_2": 1, "sf_2": 50.0, "al_2": 0.15,
"cbe_3": 1, "sf_3": 60.0, "al_3": 0.3,
"cbe_4": 1, "sf_4": 80.0, "al_4": 0.5,
"cbe_5": 1, "sf_5": 120.0, "al_5": 0.6,
"cbe_6": 1, "sf_6": 200.0, "al_6": 0.7,
"cbe_7": 1, "sf_7": 500.0, "al_7": 0.8,
"ce_0": 1, "at_0": 5.0, "rt_0": 100.0, "cr_0": 30.0, "kn_0": 0.10, "al_0": 0.000, "mk_0": 1.0, "bth_0": 1.0, "bsa_0": 1.0, "scm_0": 0,
"ce_1": 1, "at_1": 6.0, "rt_1": 110.0, "cr_1": 30.0, "kn_1": 0.10, "mk_1": 1.0, "bth_1": 1.0, "bsa_1": 1.0, "scm_1": 0,
"ce_2": 1, "at_2": 6.0, "rt_2": 100.0, "cr_2": 25.0, "kn_2": 0.12, "mk_2": 1.1, "bth_2": 1.0, "bsa_2": 1.0, "scm_2": 0,
"ce_3": 1, "at_3": 5.0, "rt_3": 90.0, "cr_3": 20.0, "kn_3": 0.12, "mk_3": 1.2, "bth_3": 1.0, "bsa_3": 1.0, "scm_3": 0,
"ce_4": 1, "at_4": 4.5, "rt_4": 80.0, "cr_4": 16.0, "kn_4": 0.15, "mk_4": 1.3, "bth_4": 1.0, "bsa_4": 1.0, "scm_4": 0,
"ce_5": 1, "at_5": 4.0, "rt_5": 60.0, "cr_5": 10.0, "kn_5": 0.20, "mk_5": 1.5, "bth_5": 1.0, "bsa_5": 1.0, "scm_5": 0,
"ce_6": 1, "at_6": 3.0, "rt_6": 40.0, "cr_6": 6.0, "kn_6": 0.30, "mk_6": 1.4, "bth_6": 1.0, "bsa_6": 1.0, "scm_6": 0,
"ce_7": 1, "at_7": 2.5, "rt_7": 30.0, "cr_7": 4.0, "kn_7": 0.40, "mk_7": 1.2, "bth_7": 1.0, "bsa_7": 1.0, "scm_7": 0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/loud_comp_mono",
"name": "ell",
"control": {
"enabled": 1,
"volume": 0.0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/loud_comp_mono",
"name": "elr",
"control": {
"enabled": 1,
"volume": 0.0
}
},
{
"type": "builtin",
"label": "copy",
"name": "copyL"
},
{
"type": "builtin",
"label": "copy",
"name": "copyR"
},
{
"type": "builtin",
"label": "convolver",
"name": "convLT",
"config": {
"filename": [
"/usr/share/t2-linux-audio/15_1/baked-tweeters-44k.wav",
"/usr/share/t2-linux-audio/15_1/baked-tweeters-48k.wav",
"/usr/share/t2-linux-audio/15_1/baked-tweeters-96k.wav"
],
"channel": 0,
"gain": 1.2
}
},
{
"type": "builtin",
"label": "convolver",
"name": "convRT",
"config": {
"filename": [
"/usr/share/t2-linux-audio/15_1/baked-tweeters-44k.wav",
"/usr/share/t2-linux-audio/15_1/baked-tweeters-48k.wav",
"/usr/share/t2-linux-audio/15_1/baked-tweeters-96k.wav"
],
"channel": 0,
"gain": 1.2
}
},
{
"type": "builtin",
"label": "convolver",
"name": "convLW",
"config": {
"filename": [
"/usr/share/t2-linux-audio/15_1/baked-woofers-44k.wav",
"/usr/share/t2-linux-audio/15_1/baked-woofers-48k.wav",
"/usr/share/t2-linux-audio/15_1/baked-woofers-96k.wav"
],
"channel": 0,
"gain": 1.2
}
},
{
"type": "builtin",
"label": "convolver",
"name": "convRW",
"config": {
"filename": [
"/usr/share/t2-linux-audio/15_1/baked-woofers-44k.wav",
"/usr/share/t2-linux-audio/15_1/baked-woofers-48k.wav",
"/usr/share/t2-linux-audio/15_1/baked-woofers-96k.wav"
],
"channel": 0,
"gain": 1.2
}
},
{
"type": "lv2",
"name": "thermal_guard",
"plugin": "http://lsp-plug.in/plugins/lv2/sc_compressor_stereo",
"control": {
"enabled": 1,
"g_in": 1.0,
"g_out": 1.0,
"at": 2500.0,
"rt": 5000.0,
"al": 0.5,
"cr": 4.0,
"kn": 0.5,
"scp": 0,
"scm": 1
}
},
{
"type": "lv2",
"name": "wlim",
"plugin": "http://plugin.org.uk/swh-plugins/fastLookaheadLimiter",
"control": {
"ingain": 0,
"limit": 0,
"release": 0.25
}
},
{
"type": "lv2",
"name": "tlim",
"plugin": "http://plugin.org.uk/swh-plugins/fastLookaheadLimiter",
"control": {
"ingain": 0,
"limit": 0,
"release": 0.15
}
}
],
"links": [
{"output": "virtualbass:out_l", "input": "thermal_guard:in_l"},
{"output": "virtualbass:out_r", "input": "thermal_guard:in_r"},
{"output": "thermal_guard:out_l", "input": "ell:in"},
{"output": "thermal_guard:out_r", "input": "elr:in"},
{"output": "ell:out", "input": "copyL:In"},
{"output": "elr:out", "input": "copyR:In"},
{"output": "copyL:Out", "input": "convLT:In"},
{"output": "copyL:Out", "input": "convLW:In"},
{"output": "copyR:Out", "input": "convRT:In"},
{"output": "copyR:Out", "input": "convRW:In"},
{"output": "convLW:Out", "input": "multiband_compressor:in_l"},
{"output": "convRW:Out", "input": "multiband_compressor:in_r"},
{"output": "multiband_compressor:out_l", "input": "wlim:in_1"},
{"output": "multiband_compressor:out_r", "input": "wlim:in_2"},
{"output": "convLT:Out", "input": "tlim:in_1"},
{"output": "convRT:Out", "input": "tlim:in_2"}
],
"inputs": [
"virtualbass:in_l",
"virtualbass:in_r"
],
"outputs": [
"wlim:out_1",
"wlim:out_2",
"tlim:out_1",
"tlim:out_2"
],
"capture.volumes": [
{
"control": "ell:volume",
"min": -65.0,
"max": 0.0,
"scale": "linear"
},
{
"control": "elr:volume",
"min": -65.0,
"max": 0.0,
"scale": "linear"
}
]
},
"capture.props": {
"node.name": "audio_effect.t2-151-speakers",
"media.class": "Audio/Sink",
"audio.channels": "2",
"audio.position": ["FL", "FR"],
"device.api": "dsp",
"audio.allowed-rates": [48000, 44100],
"node.virtual": "false",
"priority.session": 2500,
"priority.driver": 2500,
"state.default-volume": 1.0,
"device.icon-name": "audio-speakers"
},
"playback.props": {
"node.name": "effect_output.t2-151-speakers",
"node.description": "MacBook Pro 15,1 DSP Speakers Out",
"media.name": "MacBook Pro 15,1 DSP Speakers Out",
"target.object": "alsa_output.platform-sound.RawSpeakers",
"node.dont-fallback": "true",
"node.passive": "true",
"audio.channels": "4",
"audio.allowed-rates": [48000, 44100],
"audio.position": ["FL", "FR", "RL", "RR"]
}
}

View File

@@ -1,502 +0,0 @@
{
"node.description": "MacBook Pro 15,1 Audio DSP",
"media.name": "MacBook Pro 15,1 Audio DSP",
"filter.graph": {
"nodes": [
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/para_equalizer_x16_stereo",
"name": "user_eq",
"control": {
"enabled": 1,
"mode": 0,
"g_in": 1.0,
"g_out": 1.40,
"ft_0": 5,
"f_0": 70.0,
"g_0": 1.15,
"q_0": 0.7,
"s_0": 0,
"ft_1": 1,
"f_1": 110.0,
"g_1": 1.1,
"q_1": 1.0,
"ft_2": 1,
"f_2": 315.0,
"g_2": 1.05,
"q_2": 1.0,
"ft_3": 1,
"f_3": 1000.0,
"g_3": 1.01,
"q_3": 1.0,
"ft_4": 1,
"f_4": 2500.0,
"g_4": 1.00,
"q_4": 1.0,
"ft_5": 1,
"f_5": 6000.0,
"g_5": 0.92,
"q_5": 1.0,
"ft_6": 3,
"f_6": 10000.0,
"g_6": 0.85,
"q_6": 0.7,
"ft_7": 3,
"f_7": 16000.0,
"g_7": 0.80,
"q_7": 0.7,
"s_7": 0
}
},
{
"type": "lv2",
"plugin": "https://chadmed.au/bankstown",
"name": "virtualbass",
"control": {
"amt": 1.4,
"floor": 40.0,
"ceil": 120.0,
"final_hp": 55.0,
"sat_second": 1.0,
"sat_third": 1.0,
"blend": 0.30
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/para_equalizer_x16_stereo",
"name": "equalizer",
"control": {
"enabled": 1,
"mode": 0,
"g_in": 1.0,
"g_out": 1.0,
"ft_0": 5,
"f_0": 40.0,
"g_0": 2.83,
"q_0": 0.70,
"ft_1": 1,
"f_1": 80.0,
"g_1": 2.00,
"q_1": 1.00,
"ft_2": 1,
"f_2": 160.0,
"g_2": 1.41,
"q_2": 1.00,
"ft_3": 1,
"f_3": 320.0,
"g_3": 1.00,
"q_3": 1.00,
"ft_4": 1,
"f_4": 640.0,
"g_4": 0.71,
"q_4": 1.00,
"ft_5": 1,
"f_5": 1280.0,
"g_5": 0.50,
"q_5": 1.00,
"ft_6": 1,
"f_6": 2560.0,
"g_6": 0.35,
"q_6": 1.00,
"ft_7": 3,
"f_7": 12000.0,
"g_7": 0.24,
"q_7": 0.70
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/loud_comp_stereo",
"name": "loudness",
"control": {
"enabled": 1,
"input": 1.0,
"fft": 1
}
},
{
"type": "builtin",
"label": "copy",
"name": "copyL"
},
{
"type": "builtin",
"label": "copy",
"name": "copyR"
},
{
"type": "builtin",
"label": "convolver",
"name": "convLT",
"config": {
"filename": [
"/usr/share/t2-linux-audio/15_1/tweeters-44k.wav",
"/usr/share/t2-linux-audio/15_1/tweeters-48k.wav",
"/usr/share/t2-linux-audio/15_1/tweeters-96k.wav"
],
"channel": 0,
"gain": 1.1
}
},
{
"type": "builtin",
"label": "convolver",
"name": "convRT",
"config": {
"filename": [
"/usr/share/t2-linux-audio/15_1/tweeters-44k.wav",
"/usr/share/t2-linux-audio/15_1/tweeters-48k.wav",
"/usr/share/t2-linux-audio/15_1/tweeters-96k.wav"
],
"channel": 0,
"gain": 1.1
}
},
{
"type": "builtin",
"label": "convolver",
"name": "convLW",
"config": {
"filename": [
"/usr/share/t2-linux-audio/15_1/woofers-44k.wav",
"/usr/share/t2-linux-audio/15_1/woofers-48k.wav",
"/usr/share/t2-linux-audio/15_1/woofers-96k.wav"
],
"channel": 0,
"gain": 1.2
}
},
{
"type": "builtin",
"label": "convolver",
"name": "convRW",
"config": {
"filename": [
"/usr/share/t2-linux-audio/15_1/woofers-44k.wav",
"/usr/share/t2-linux-audio/15_1/woofers-48k.wav",
"/usr/share/t2-linux-audio/15_1/woofers-96k.wav"
],
"channel": 0,
"gain": 1.2
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/mb_compressor_stereo",
"name": "wmb_comp",
"control": {
"enabled": 1,
"mode": 1,
"g_in": 1.0,
"g_out": 1.1,
"g_dry": 0.0001,
"g_wet": 1.0,
"cbe_1": 1,
"sf_1": 40.0,
"cbe_2": 1,
"sf_2": 50.0,
"cbe_3": 1,
"sf_3": 60.0,
"cbe_4": 1,
"sf_4": 80.0,
"cbe_5": 1,
"sf_5": 120.0,
"cbe_6": 1,
"sf_6": 200.0,
"cbe_7": 1,
"sf_7": 500.0,
"ce_0": 1,
"at_0": 5.0,
"rt_0": 100.0,
"cr_0": 30.0,
"kn_0": 0.10,
"al_0": 0.95,
"mk_0": 1.0,
"bth_0": 1.0,
"bsa_0": 1.0,
"scm_0": 0,
"ce_1": 1,
"at_1": 6.0,
"rt_1": 110.0,
"cr_1": 30.0,
"kn_1": 0.10,
"al_1": 0.92,
"mk_1": 1.0,
"bth_1": 1.0,
"bsa_1": 1.0,
"scm_1": 0,
"ce_2": 1,
"at_2": 6.0,
"rt_2": 100.0,
"cr_2": 25.0,
"kn_2": 0.12,
"al_2": 0.90,
"mk_2": 1.0,
"bth_2": 1.0,
"bsa_2": 1.0,
"scm_2": 0,
"ce_3": 1,
"at_3": 5.0,
"rt_3": 90.0,
"cr_3": 20.0,
"kn_3": 0.12,
"al_3": 0.88,
"mk_3": 1.0,
"bth_3": 1.0,
"bsa_3": 1.0,
"scm_3": 0,
"ce_4": 1,
"at_4": 4.5,
"rt_4": 80.0,
"cr_4": 16.0,
"kn_4": 0.15,
"al_4": 0.85,
"mk_4": 1.0,
"bth_4": 1.0,
"bsa_4": 1.0,
"scm_4": 0,
"ce_5": 1,
"at_5": 4.0,
"rt_5": 60.0,
"cr_5": 10.0,
"kn_5": 0.20,
"al_5": 0.80,
"mk_5": 1.0,
"bth_5": 1.0,
"bsa_5": 1.0,
"scm_5": 0,
"ce_6": 1,
"at_6": 3.0,
"rt_6": 40.0,
"cr_6": 6.0,
"kn_6": 0.30,
"al_6": 0.80,
"mk_6": 1.0,
"bth_6": 1.0,
"bsa_6": 1.0,
"scm_6": 0,
"ce_7": 1,
"at_7": 2.5,
"rt_7": 30.0,
"cr_7": 4.0,
"kn_7": 0.40,
"al_7": 0.80,
"mk_7": 1.0,
"bth_7": 1.0,
"bsa_7": 1.0,
"scm_7": 0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/mb_compressor_stereo",
"name": "tmb_comp",
"control": {
"enabled": 1,
"mode": 1,
"g_in": 1.0,
"g_out": 1.0,
"g_dry": 0.0001,
"g_wet": 1.0,
"cbe_1": 1,
"sf_1": 2500.0,
"cbe_2": 1,
"sf_2": 6000.0,
"cbe_3": 1,
"sf_3": 12000.0,
"ce_0": 1,
"at_0": 3.0,
"rt_0": 50.0,
"cr_0": 8.0,
"kn_0": 0.20,
"al_0": 0.85,
"mk_0": 1.0,
"bth_0": 1.0,
"bsa_0": 1.0,
"scm_0": 0,
"ce_1": 1,
"at_1": 2.5,
"rt_1": 40.0,
"cr_1": 6.0,
"kn_1": 0.30,
"al_1": 0.80,
"mk_1": 1.0,
"bth_1": 1.0,
"bsa_1": 1.0,
"scm_1": 0,
"ce_2": 1,
"at_2": 2.0,
"rt_2": 30.0,
"cr_2": 4.0,
"kn_2": 0.40,
"al_2": 0.75,
"mk_2": 1.0,
"bth_2": 1.0,
"bsa_2": 1.0,
"scm_2": 0,
"ce_3": 1,
"at_3": 1.5,
"rt_3": 20.0,
"cr_3": 3.0,
"kn_3": 0.50,
"al_3": 0.70,
"mk_3": 1.0,
"bth_3": 1.0,
"bsa_3": 1.0,
"scm_3": 0
}
},
{
"type": "lv2",
"name": "wlim",
"plugin": "http://plugin.org.uk/swh-plugins/fastLookaheadLimiter",
"control": {
"ingain": 0,
"limit": 0,
"release": 0.25
}
},
{
"type": "lv2",
"name": "tlim",
"plugin": "http://plugin.org.uk/swh-plugins/fastLookaheadLimiter",
"control": {
"ingain": 0,
"limit": -1,
"release": 0.15
}
}
],
"links": [
{
"output": "user_eq:out_l",
"input": "virtualbass:in_l"
},
{
"output": "user_eq:out_r",
"input": "virtualbass:in_r"
},
{
"output": "virtualbass:out_l",
"input": "equalizer:in_l"
},
{
"output": "virtualbass:out_r",
"input": "equalizer:in_r"
},
{
"output": "equalizer:out_l",
"input": "loudness:in_l"
},
{
"output": "equalizer:out_r",
"input": "loudness:in_r"
},
{
"output": "loudness:out_l",
"input": "copyL:In"
},
{
"output": "loudness:out_r",
"input": "copyR:In"
},
{
"output": "copyL:Out",
"input": "convLT:In"
},
{
"output": "copyL:Out",
"input": "convLW:In"
},
{
"output": "copyR:Out",
"input": "convRT:In"
},
{
"output": "copyR:Out",
"input": "convRW:In"
},
{
"output": "convLW:Out",
"input": "wmb_comp:in_l"
},
{
"output": "convRW:Out",
"input": "wmb_comp:in_r"
},
{
"output": "wmb_comp:out_l",
"input": "wlim:in_1"
},
{
"output": "wmb_comp:out_r",
"input": "wlim:in_2"
},
{
"output": "convLT:Out",
"input": "tmb_comp:in_l"
},
{
"output": "convRT:Out",
"input": "tmb_comp:in_r"
},
{
"output": "tmb_comp:out_l",
"input": "tlim:in_1"
},
{
"output": "tmb_comp:out_r",
"input": "tlim:in_2"
}
],
"inputs": [
"user_eq:in_l",
"user_eq:in_r"
],
"outputs": [
"wlim:out_1",
"wlim:out_2",
"tlim:out_1",
"tlim:out_2"
]
},
"capture.props": {
"node.name": "audio_effect.t2-151-speakers",
"media.class": "Audio/Sink",
"audio.channels": "2",
"audio.position": [
"FL",
"FR"
],
"device.api": "dsp",
"audio.allowed-rates": [
48000,
44100
],
"node.virtual": "false",
"priority.session": 2500,
"priority.driver": 2500,
"state.default-volume": 1.0,
"device.icon-name": "audio-speakers"
},
"playback.props": {
"node.name": "effect_output.t2-151-speakers",
"node.description": "MacBook Pro 15,1 DSP Speakers Out",
"media.name": "MacBook Pro 15,1 DSP Speakers Out",
"target.object": "alsa_output.platform-sound.RawSpeakers",
"node.dont-fallback": "true",
"node.passive": "true",
"audio.channels": "4",
"audio.allowed-rates": [
48000,
44100
],
"audio.position": [
"FL",
"FR",
"RL",
"RR"
]
}
}

View File

@@ -1,6 +1,6 @@
{
"node.description": "MacBook Pro 15,1 Audio DSP",
"media.name": "MacBook Pro 15,1 Audio DSP",
"node.description": "MacBook Pro 15,1 DSP Speakers",
"media.name": "MacBook Pro 15,1 DSP Speakers",
"filter.graph": {
"nodes": [
{
@@ -9,28 +9,14 @@
"name": "user_eq",
"control": {
"enabled": 1, "mode": 0, "g_in": 1.0, "g_out": 1.5,
"ft_0": 5, "f_0": 70.0, "g_0": 1.00, "q_0": 0.70, "s_0": 0,
"ft_1": 1, "f_1": 110.0, "g_1": 1.00, "q_1": 1.00,
"ft_2": 1, "f_2": 315.0, "g_2": 1.00, "q_2": 1.00,
"ft_3": 1, "f_3": 600.0, "g_3": 1.00, "q_3": 1.00,
"ft_4": 1, "f_4": 1000.0, "g_4": 1.00, "q_4": 1.00,
"ft_5": 1, "f_5": 2500.0, "g_5": 1.00, "q_5": 1.00,
"ft_6": 1, "f_6": 6000.0, "g_6": 1.00, "q_6": 1.00,
"ft_7": 3, "f_7": 12000.0,"g_7": 1.00, "q_7": 0.70, "s_7": 0
}
},
{
"type": "lv2",
"plugin": "https://chadmed.au/bankstown",
"name": "virtualbass",
"control": {
"amt": 1.4,
"floor": 40.0,
"ceil": 120.0,
"final_hp": 55.0,
"sat_second": 1.0,
"sat_third": 1.0,
"blend": 0.30
"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": 0.95, "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
}
},
{
@@ -38,25 +24,86 @@
"plugin": "http://lsp-plug.in/plugins/lv2/para_equalizer_x16_stereo",
"name": "equalizer",
"control": {
"enabled": 1, "mode": 0, "g_in": 1.0, "g_out": 1.0,
"ft_0": 5, "f_0": 40.0, "g_0": 2.83, "q_0": 0.70,
"ft_1": 1, "f_1": 80.0, "g_1": 2.00, "q_1": 1.00,
"ft_2": 1, "f_2": 160.0, "g_2": 1.41, "q_2": 1.00,
"ft_3": 1, "f_3": 320.0, "g_3": 1.00, "q_3": 1.00,
"ft_4": 1, "f_4": 640.0, "g_4": 0.71, "q_4": 1.00,
"ft_5": 1, "f_5": 1280.0, "g_5": 0.50, "q_5": 1.00,
"ft_6": 1, "f_6": 2560.0, "g_6": 0.35, "q_6": 1.00,
"ft_7": 3, "f_7": 12000.0,"g_7": 0.24, "q_7": 0.70
"enabled": 1,
"mode": 0,
"g_in": 1.0,
"g_out": 1.5,
"ft_0": 2, "f_0": 20.0, "g_0": 2.5637, "q_0": 1.41, "s_0": 3, "xm_0": 0, "fm_0": 0,
"ft_1": 1, "f_1": 31.5, "g_1": 2.2448, "q_1": 1.41, "s_1": 0, "xm_1": 0, "fm_1": 0,
"ft_2": 1, "f_2": 50.0, "g_2": 1.9610, "q_2": 1.41, "s_2": 0, "xm_2": 0, "fm_2": 0,
"ft_3": 1, "f_3": 80.0, "g_3": 1.7091, "q_3": 1.41, "s_3": 0, "xm_3": 0, "fm_3": 0,
"ft_4": 1, "f_4": 125.0, "g_4": 1.5, "q_4": 1.41, "s_4": 0, "xm_4": 0, "fm_4": 0,
"ft_5": 1, "f_5": 200.0, "g_5": 1.3073, "q_5": 1.41, "s_5": 0, "xm_5": 0, "fm_5": 0,
"ft_6": 1, "f_6": 315.0, "g_6": 1.1447, "q_6": 1.41, "s_6": 0, "xm_6": 0, "fm_6": 0,
"ft_7": 1, "f_7": 500.0, "g_7": 1.0, "q_7": 1.41, "s_7": 0, "xm_7": 0, "fm_7": 0,
"ft_8": 1, "f_8": 800.0, "g_8": 0.8716, "q_8": 1.41, "s_8": 0, "xm_8": 0, "fm_8": 0,
"ft_9": 1, "f_9": 1250.0, "g_9": 0.7649, "q_9": 1.41, "s_9": 0, "xm_9": 0, "fm_9": 0,
"ft_10": 1, "f_10": 2000.0, "g_10": 0.6667, "q_10": 1.41, "s_10": 0, "xm_10": 0, "fm_10": 0,
"ft_11": 1, "f_11": 3150.0, "g_11": 0.5837, "q_11": 1.41, "s_11": 0, "xm_11": 0, "fm_11": 0,
"ft_12": 1, "f_12": 5000.0, "g_12": 0.5099, "q_12": 1.41, "s_12": 0, "xm_12": 0, "fm_12": 0,
"ft_13": 1, "f_13": 8000.0, "g_13": 0.4444, "q_13": 1.41, "s_13": 0, "xm_13": 0, "fm_13": 0,
"ft_14": 1, "f_14": 12500.0, "g_14": 0.3901, "q_14": 1.41, "s_14": 0, "xm_14": 0, "fm_14": 0,
"ft_15": 1, "f_15": 20000.0, "g_15": 0.3400, "q_15": 1.41, "s_15": 0, "xm_15": 0, "fm_15": 0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/loud_comp_stereo",
"name": "loudness",
"plugin": "https://chadmed.au/bankstown",
"name": "virtualbass",
"control": {
"bypass": 0,
"amt": 1.0,
"sat_second": 1.0,
"sat_third": 1.0,
"blend": 1.0,
"ceil": 150.0,
"floor": 60.0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/mb_compressor_stereo",
"name": "multiband_compressor",
"control": {
"enabled": 1,
"input": 1.0,
"fft": 1
"mode": 1,
"g_in": 1.0,
"g_out": 1.25,
"g_dry": 0.0001,
"g_wet": 1.0,
"cbe_1": 1, "sf_1": 40.0,
"cbe_2": 1, "sf_2": 50.0,
"cbe_3": 1, "sf_3": 60.0,
"cbe_4": 1, "sf_4": 80.0,
"cbe_5": 1, "sf_5": 120.0,
"cbe_6": 1, "sf_6": 200.0,
"cbe_7": 1, "sf_7": 500.0,
"ce_0": 1, "at_0": 5.0, "rt_0": 100.0, "cr_0": 30.0, "kn_0": 0.10, "al_0": 0.025, "mk_0": 1.0, "bth_0": 1.0, "bsa_0": 1.0, "scm_0": 0,
"ce_1": 1, "at_1": 6.0, "rt_1": 110.0, "cr_1": 30.0, "kn_1": 0.10, "al_1": 0.050, "mk_1": 1.0, "bth_1": 1.0, "bsa_1": 1.0, "scm_1": 0,
"ce_2": 1, "at_2": 6.0, "rt_2": 100.0, "cr_2": 25.0, "kn_2": 0.12, "al_2": 0.070, "mk_2": 1.1, "bth_2": 1.0, "bsa_2": 1.0, "scm_2": 0,
"ce_3": 1, "at_3": 5.0, "rt_3": 90.0, "cr_3": 20.0, "kn_3": 0.12, "al_3": 0.090, "mk_3": 1.2, "bth_3": 1.0, "bsa_3": 1.0, "scm_3": 0,
"ce_4": 1, "at_4": 4.5, "rt_4": 80.0, "cr_4": 16.0, "kn_4": 0.15, "al_4": 0.120, "mk_4": 1.3, "bth_4": 1.0, "bsa_4": 1.0, "scm_4": 0,
"ce_5": 1, "at_5": 4.0, "rt_5": 60.0, "cr_5": 10.0, "kn_5": 0.20, "al_5": 0.250, "mk_5": 1.5, "bth_5": 1.0, "bsa_5": 1.0, "scm_5": 0,
"ce_6": 1, "at_6": 3.0, "rt_6": 40.0, "cr_6": 6.0, "kn_6": 0.30, "al_6": 0.300, "mk_6": 1.4, "bth_6": 1.0, "bsa_6": 1.0, "scm_6": 0,
"ce_7": 1, "at_7": 2.5, "rt_7": 30.0, "cr_7": 4.0, "kn_7": 0.40, "al_7": 0.300, "mk_7": 1.2, "bth_7": 1.0, "bsa_7": 1.0, "scm_7": 0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/loud_comp_mono",
"name": "ell",
"control": {
"enabled": 1,
"volume": 0.0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/loud_comp_mono",
"name": "elr",
"control": {
"enabled": 1,
"volume": 0.0
}
},
{
@@ -127,50 +174,19 @@
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/mb_compressor_stereo",
"name": "wmb_comp",
"name": "thermal_guard",
"plugin": "http://lsp-plug.in/plugins/lv2/sc_compressor_stereo",
"control": {
"enabled": 1,
"mode": 1,
"g_in": 1.0,
"g_out": 1.0,
"g_dry": 0.0001,
"g_wet": 1.0,
"cbe_1": 1, "sf_1": 40.0,
"cbe_2": 1, "sf_2": 50.0,
"cbe_3": 1, "sf_3": 60.0,
"cbe_4": 1, "sf_4": 80.0,
"cbe_5": 1, "sf_5": 120.0,
"cbe_6": 1, "sf_6": 200.0,
"cbe_7": 1, "sf_7": 500.0,
"ce_0": 1, "at_0": 5.0, "rt_0": 100.0, "cr_0": 30.0, "kn_0": 0.10, "al_0": 0.95, "mk_0": 1.0, "bth_0": 1.0, "bsa_0": 1.0, "scm_0": 0,
"ce_1": 1, "at_1": 6.0, "rt_1": 110.0, "cr_1": 30.0, "kn_1": 0.10, "al_1": 0.92, "mk_1": 1.0, "bth_1": 1.0, "bsa_1": 1.0, "scm_1": 0,
"ce_2": 1, "at_2": 6.0, "rt_2": 100.0, "cr_2": 25.0, "kn_2": 0.12, "al_2": 0.90, "mk_2": 1.0, "bth_2": 1.0, "bsa_2": 1.0, "scm_2": 0,
"ce_3": 1, "at_3": 5.0, "rt_3": 90.0, "cr_3": 20.0, "kn_3": 0.12, "al_3": 0.88, "mk_3": 1.0, "bth_3": 1.0, "bsa_3": 1.0, "scm_3": 0,
"ce_4": 1, "at_4": 4.5, "rt_4": 80.0, "cr_4": 16.0, "kn_4": 0.15, "al_4": 0.85, "mk_4": 1.0, "bth_4": 1.0, "bsa_4": 1.0, "scm_4": 0,
"ce_5": 1, "at_5": 4.0, "rt_5": 60.0, "cr_5": 10.0, "kn_5": 0.20, "al_5": 0.80, "mk_5": 1.0, "bth_5": 1.0, "bsa_5": 1.0, "scm_5": 0,
"ce_6": 1, "at_6": 3.0, "rt_6": 40.0, "cr_6": 6.0, "kn_6": 0.30, "al_6": 0.80, "mk_6": 1.0, "bth_6": 1.0, "bsa_6": 1.0, "scm_6": 0,
"ce_7": 1, "at_7": 2.5, "rt_7": 30.0, "cr_7": 4.0, "kn_7": 0.40, "al_7": 0.80, "mk_7": 1.0, "bth_7": 1.0, "bsa_7": 1.0, "scm_7": 0
}
},
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/mb_compressor_stereo",
"name": "tmb_comp",
"control": {
"enabled": 1,
"mode": 1,
"g_in": 1.0,
"g_out": 1.0,
"g_dry": 0.0001,
"g_wet": 1.0,
"cbe_1": 1, "sf_1": 2500.0,
"cbe_2": 1, "sf_2": 6000.0,
"cbe_3": 1, "sf_3": 12000.0,
"ce_0": 1, "at_0": 3.0, "rt_0": 50.0, "cr_0": 8.0, "kn_0": 0.20, "al_0": 0.85, "mk_0": 1.0, "bth_0": 1.0, "bsa_0": 1.0, "scm_0": 0,
"ce_1": 1, "at_1": 2.5, "rt_1": 40.0, "cr_1": 6.0, "kn_1": 0.30, "al_1": 0.80, "mk_1": 1.0, "bth_1": 1.0, "bsa_1": 1.0, "scm_1": 0,
"ce_2": 1, "at_2": 2.0, "rt_2": 30.0, "cr_2": 4.0, "kn_2": 0.40, "al_2": 0.75, "mk_2": 1.0, "bth_2": 1.0, "bsa_2": 1.0, "scm_2": 0,
"ce_3": 1, "at_3": 1.5, "rt_3": 20.0, "cr_3": 3.0, "kn_3": 0.50, "al_3": 0.70, "mk_3": 1.0, "bth_3": 1.0, "bsa_3": 1.0, "scm_3": 0
"at": 2500.0,
"rt": 5000.0,
"al": 0.4,
"cr": 4.0,
"kn": 0.5,
"scp": 0,
"scm": 1
}
},
{
@@ -179,42 +195,61 @@
"plugin": "http://plugin.org.uk/swh-plugins/fastLookaheadLimiter",
"control": {
"ingain": 0,
"limit": -2,
"limit": 0,
"release": 0.25
}
},
{
"type": "lv2",
"name": "tweeter_thermal_guard",
"plugin": "http://lsp-plug.in/plugins/lv2/sc_compressor_stereo",
"control": {
"enabled": 1,
"g_in": 1.0,
"g_out": 1.0,
"at": 1000.0,
"rt": 2000.0,
"al": 0.126491,
"cr": 4.0,
"kn": 0.5,
"scp": 0,
"scm": 1
}
},
{
"type": "lv2",
"name": "tlim",
"plugin": "http://plugin.org.uk/swh-plugins/fastLookaheadLimiter",
"control": {
"ingain": 0,
"limit": -1,
"limit": 0,
"release": 0.15
}
}
],
"links": [
{"output": "user_eq:out_l", "input": "virtualbass:in_l"},
{"output": "user_eq:out_r", "input": "virtualbass:in_r"},
{"output": "virtualbass:out_l", "input": "equalizer:in_l"},
{"output": "virtualbass:out_r", "input": "equalizer:in_r"},
{"output": "equalizer:out_l", "input": "loudness:in_l"},
{"output": "equalizer:out_r", "input": "loudness:in_r"},
{"output": "loudness:out_l", "input": "copyL:In"},
{"output": "loudness:out_r", "input": "copyR:In"},
{"output": "user_eq:out_l", "input": "equalizer:in_l"},
{"output": "user_eq:out_r", "input": "equalizer:in_r"},
{"output": "equalizer:out_l", "input": "virtualbass:in_l"},
{"output": "equalizer:out_r", "input": "virtualbass:in_r"},
{"output": "virtualbass:out_l", "input": "multiband_compressor:in_l"},
{"output": "virtualbass:out_r", "input": "multiband_compressor:in_r"},
{"output": "multiband_compressor:out_l", "input": "ell:in"},
{"output": "multiband_compressor:out_r", "input": "elr:in"},
{"output": "ell:out", "input": "copyL:In"},
{"output": "elr:out", "input": "copyR:In"},
{"output": "copyL:Out", "input": "convLT:In"},
{"output": "copyL:Out", "input": "convLW:In"},
{"output": "copyR:Out", "input": "convRT:In"},
{"output": "copyR:Out", "input": "convRW:In"},
{"output": "convLW:Out", "input": "wmb_comp:in_l"},
{"output": "convRW:Out", "input": "wmb_comp:in_r"},
{"output": "wmb_comp:out_l", "input": "wlim:in_1"},
{"output": "wmb_comp:out_r", "input": "wlim:in_2"},
{"output": "convLT:Out", "input": "tmb_comp:in_l"},
{"output": "convRT:Out", "input": "tmb_comp:in_r"},
{"output": "tmb_comp:out_l", "input": "tlim:in_1"},
{"output": "tmb_comp:out_r", "input": "tlim:in_2"}
{"output": "convLW:Out", "input": "thermal_guard:in_l"},
{"output": "convRW:Out", "input": "thermal_guard:in_r"},
{"output": "thermal_guard:out_l", "input": "wlim:in_1"},
{"output": "thermal_guard:out_r", "input": "wlim:in_2"},
{"output": "convLT:Out", "input": "tweeter_thermal_guard:in_l"},
{"output": "convRT:Out", "input": "tweeter_thermal_guard:in_r"},
{"output": "tweeter_thermal_guard:out_l", "input": "tlim:in_1"},
{"output": "tweeter_thermal_guard:out_r", "input": "tlim:in_2"}
],
"inputs": [
"user_eq:in_l",
@@ -225,6 +260,20 @@
"wlim:out_2",
"tlim:out_1",
"tlim:out_2"
],
"capture.volumes": [
{
"control": "ell:volume",
"min": -65.0,
"max": 0.0,
"scale": "linear"
},
{
"control": "elr:volume",
"min": -65.0,
"max": 0.0,
"scale": "linear"
}
]
},
"capture.props": {

View File

@@ -1,15 +0,0 @@
{
"id": "apple_mbp15_1",
"name": "MacBook Pro 15,1 (2018/2019)",
"vendor": "Apple Inc.",
"dmi_matches": [
"MacBookPro15,1",
"MacBookPro15,3"
],
"sys_dir": "/usr/share/t2-linux-audio/15_1",
"channels": 4,
"crossover_freq_hz": 180.0,
"woofer_tail_taps": 16384,
"tweeter_tail_taps": 8192,
"lead_trim_ms": 5.0
}

View File

@@ -1,11 +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
"enabled": 0, "mode": 0, "g_in": 1.0, "g_out": 1.00,
"ft_0": 5, "f_0": 70.0 , "g_0": 1.1, "q_0": 0.7, "s_0": 0,
"ft_1": 1, "f_1": 110.0 , "g_1": 1.1, "q_1": 1.0,
"ft_2": 1, "f_2": 315.0 , "g_2": 1.05, "q_2": 1.0,
"ft_3": 1, "f_3": 1000.0 , "g_3": 1.01, "q_3": 1.0,
"ft_4": 1, "f_4": 2500.0 , "g_4": 0.98, "q_4": 1.0,
"ft_5": 1, "f_5": 6000.0 , "g_5": 0.97, "q_5": 1.0,
"ft_6": 3, "f_6": 10000.0, "g_6": 0.96, "q_6": 0.7,
"ft_7": 3, "f_7": 16000.0, "g_7": 0.95, "q_7": 0.7, "s_7": 0
}