audio: add eq parser

This commit is contained in:
Lucy Hochkamp 2025-09-11 13:25:42 +02:00
parent 35c8ebb56a
commit e865cd7c8f
No known key found for this signature in database

View file

@ -4,12 +4,193 @@
lib,
...
}:
with lib;
let
cfg = config.xyno.desktop.audio;
json = pkgs.formats.json { };
toFloat = builtins.fromJSON;
readFilterTxt =
text:
let
lines = splitString "\n" text;
consumeLine =
line:
let
parts = splitString " " line;
content = foldl (
props: str:
if props.lastNode == "Filter" then
props
// {
nr = toInt (removeSuffix ":" str);
lastNode = str;
}
else if props.lastNode == "Preamp:" then
props
// {
nr = 0;
gain = toFloat str;
label = "HSC";
lastNode = str;
freq = 0;
q = 1.0;
}
else if props.lastNode == "ON" then
props
// {
label = str;
lastNode = str;
}
else if props.lastNode == "Fc" then
props
// {
freq = toInt str;
lastNode = str;
}
else if props.lastNode == "Gain" then
props
// {
gain = toFloat str;
lastNode = str;
}
else if props.lastNode == "Q" then
props
// {
q = toFloat str;
lastNode = str;
}
else
props // { lastNode = str; }
) { lastNode = null; } parts;
in
content;
in
filter (x: x ? nr) (map consumeLine lines);
genFilterGraph = filters: {
nodes = (
map (filter: {
type = "builtin";
name = "eq_band_${toString (filter.nr + 1)}";
label =
if filter.label == "HSC" then
"bq_highshelf"
else if filter.label == "LSC" then
"bq_lowshelf"
else
"bq_peaking";
control = {
Freq = filter.freq;
Q = filter.q;
Gain = filter.gain;
};
}) filters
);
links = map (x: {
output = "eq_band_${toString x}:Out";
input = "eq_band_${toString (x + 1)}:In";
}) (range 1 ((builtins.length filters) - 1));
};
genfilterChain =
cfg':
json.generate "filter-chain.json" (
recursiveUpdate {
"node.description" = cfg'.displayName;
"media.name" = cfg'.displayName;
"filter.graph" = genFilterGraph (readFilterTxt cfg'.filterText);
"audio.channels" = 2;
"audio.position" = [
"FL"
"FR"
];
"capture.props" = {
"node.name" = "effect_input.${cfg'.internalName}";
"media.class" = "Audio/Sink";
};
"playback.props" = {
"node.name" = "effect_output.${cfg'.internalName}";
"node.passive" = true;
"target.object" = cfg'.hwAddr;
};
} cfg'.extraConfig
);
eqPkg =
cfg':
(pkgs.writeTextDir "share/wireplumber/wireplumber.conf.d/99-${cfg'.internalName}.conf" ''
node.software-dsp.rules = [
{
matches = [{ node.name = "${cfg'.hwAddr}" }]
actions = {
create-filter = {
filter-path = "${genfilterChain cfg'}"
hide-parent = ${boolToString cfg'.hideParent}
}
}
}
]
wireplumber.profiles = {
main = { node.software-dsp = "required" }
}
'')
// {
passthru.requiredLv2Packages = with pkgs; [
lsp-plugins
bankstown-lv2
];
};
eqSubmodule =
{ name, ... }:
{
options = {
internalName = mkOption {
type = types.str;
default = name;
internal = true;
};
displayName = mkOption { type = types.str; };
hwAddr = mkOption { type = types.str; };
filterText = mkOption { type = types.str; };
hideParent = mkOption {
type = types.bool;
default = true;
};
extraConfig = mkOption {
type = types.attrsOf types.anything;
default = { };
};
};
};
in
{
options.xyno.desktop.audio.enable = lib.mkEnableOption "enable pipewire and stuff";
config = lib.mkIf cfg.enable {
options.xyno.desktop.audio.enable = mkEnableOption "enable pipewire and stuff";
options.xyno.desktop.audio.eq = mkOption {
type = types.attrsOf (types.submodule eqSubmodule);
default = { };
};
config = mkIf cfg.enable {
xyno.desktop.audio.eq.se215 = {
displayName = "Shure SE215";
hwAddr = "alsa_output.usb-Synaptics_CX31993_384Khz_HIFI_AUDIO-00.analog-stereo";
extraConfig."capture.props"."device.icon-name" = "audio-headphones";
filterText = ''
Preamp: -5.7 dB
Filter 1: ON LSC Fc 105 Hz Gain 0.3 dB Q 0.70
Filter 2: ON PK Fc 174 Hz Gain -4.6 dB Q 0.68
Filter 3: ON PK Fc 811 Hz Gain 3.1 dB Q 1.42
Filter 4: ON PK Fc 3430 Hz Gain 3.8 dB Q 4.37
Filter 5: ON PK Fc 6943 Hz Gain 5.4 dB Q 2.76
Filter 6: ON HSC Fc 10000 Hz Gain 3.8 dB Q 0.70
Filter 7: ON PK Fc 5725 Hz Gain 8.1 dB Q 1.13
Filter 8: ON PK Fc 5056 Hz Gain -10.9 dB Q 2.93
Filter 9: ON PK Fc 7269 Hz Gain -6.3 dB Q 3.47
Filter 10: ON PK Fc 2278 Hz Gain -2.1 dB Q 2.94
'';
};
services.pipewire = {
enable = true;
# raopOpenFirewall = true; # airplay
@ -20,7 +201,9 @@ in
"default.clock.allowed-rates" = [
"44100"
"48000"
"88200"
"96000"
"176400"
"192000"
];
};
@ -44,6 +227,9 @@ in
];
};
};
wireplumber.extraConfig."98-bluetooth"."wireplumber.settings"."bluetooth.autoswitch-to-headset-profile" =
false;
wireplumber.configPackages = mapAttrsToList (n: v: eqPkg v) cfg.eq;
};
};