Add virtual 4.1 surround sound configuration for zix790prors
Create a PipeWire virtual surround sink that routes audio to multiple physical outputs: - FL/FR channels → AmazonBasics USB speaker - RL/RR channels → Fosi BT20A PRO Bluetooth speaker - LFE channel → AmazonBasics (duplicated to both channels) Uses loopback modules with systemd services to maintain correct routing, as PipeWire's target.object parameter doesn't auto-connect properly. A timer checks every 10 seconds and fixes incorrect connections. Configuration is machine-specific and isolated in virtual-surround.nix.
This commit is contained in:
132
machines/zix790prors/virtual-surround.nix
Normal file
132
machines/zix790prors/virtual-surround.nix
Normal file
@@ -0,0 +1,132 @@
|
||||
# Virtual 4.1 surround sound setup
|
||||
# Routes FL/FR to AmazonBasics USB speaker, RL/RR to Fosi BT20A PRO Bluetooth speaker
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
services.pipewire.extraConfig.pipewire."10-virtual-surround" = {
|
||||
"context.objects" = [
|
||||
{
|
||||
factory = "adapter";
|
||||
args = {
|
||||
"factory.name" = "support.null-audio-sink";
|
||||
"node.name" = "virtual_surround_sink";
|
||||
"node.description" = "Virtual 4.1 Surround (AmazonBasics + Fosi)";
|
||||
"media.class" = "Audio/Sink";
|
||||
"audio.position" = [ "FL" "FR" "RL" "RR" "LFE" ];
|
||||
"monitor.channel-volumes" = true;
|
||||
};
|
||||
}
|
||||
];
|
||||
"context.modules" = [
|
||||
{
|
||||
name = "libpipewire-module-loopback";
|
||||
args = {
|
||||
"node.description" = "Route Front to AmazonBasics";
|
||||
"capture.props" = {
|
||||
"node.name" = "route_front_capture";
|
||||
"audio.position" = [ "FL" "FR" ];
|
||||
"stream.dont-remix" = true;
|
||||
"node.passive" = true;
|
||||
};
|
||||
"playback.props" = {
|
||||
"node.name" = "route_front_playback";
|
||||
"node.target" = "alsa_output.usb-C-Media_Electronics_Inc._AmazonBasics_Professional_Mic_2-00.analog-stereo";
|
||||
"audio.position" = [ "FL" "FR" ];
|
||||
"stream.dont-remix" = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "libpipewire-module-loopback";
|
||||
args = {
|
||||
"node.description" = "Route Rear to Fosi Audio";
|
||||
"capture.props" = {
|
||||
"node.name" = "route_rear_capture";
|
||||
"audio.position" = [ "RL" "RR" ];
|
||||
"stream.dont-remix" = true;
|
||||
"node.passive" = true;
|
||||
};
|
||||
"playback.props" = {
|
||||
"node.name" = "route_rear_playback";
|
||||
"node.target" = "bluez_output.F4_4E_FD_FB_58_62.1";
|
||||
"audio.position" = [ "FL" "FR" ];
|
||||
"stream.dont-remix" = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "libpipewire-module-loopback";
|
||||
args = {
|
||||
"node.description" = "Route Subwoofer to AmazonBasics";
|
||||
"capture.props" = {
|
||||
"node.name" = "route_lfe_capture";
|
||||
"audio.position" = [ "LFE" ];
|
||||
"stream.dont-remix" = true;
|
||||
"node.passive" = true;
|
||||
};
|
||||
"playback.props" = {
|
||||
"node.name" = "route_lfe_playback";
|
||||
"node.target" = "alsa_output.usb-C-Media_Electronics_Inc._AmazonBasics_Professional_Mic_2-00.analog-stereo";
|
||||
"audio.position" = [ "MONO" ];
|
||||
"stream.dont-remix" = false;
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# Systemd services to fix PipeWire loopback routing for virtual surround
|
||||
systemd.user.services.pipewire-surround-link = {
|
||||
description = "Link virtual surround sink to loopback captures";
|
||||
after = [ "pipewire.service" "wireplumber.service" ];
|
||||
requires = [ "pipewire.service" "wireplumber.service" ];
|
||||
wantedBy = [ "pipewire.service" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = false;
|
||||
ExecStart = pkgs.writeShellScript "surround-link" ''
|
||||
sleep 2
|
||||
# Disconnect wrong connections
|
||||
${pkgs.pipewire}/bin/pw-link -d alsa_input.pci-0000_00_1f.3.pro-input-2:capture_AUX0 route_front_capture:input_FL 2>/dev/null || true
|
||||
${pkgs.pipewire}/bin/pw-link -d alsa_input.pci-0000_00_1f.3.pro-input-2:capture_AUX1 route_front_capture:input_FR 2>/dev/null || true
|
||||
${pkgs.pipewire}/bin/pw-link -d alsa_input.pci-0000_00_1f.3.pro-input-2:capture_AUX0 route_rear_capture:input_RL 2>/dev/null || true
|
||||
${pkgs.pipewire}/bin/pw-link -d alsa_input.pci-0000_00_1f.3.pro-input-2:capture_AUX1 route_rear_capture:input_RR 2>/dev/null || true
|
||||
${pkgs.pipewire}/bin/pw-link -d alsa_input.pci-0000_00_1f.3.pro-input-2:capture_AUX0 route_lfe_capture:input_LFE 2>/dev/null || true
|
||||
# Create correct connections
|
||||
${pkgs.pipewire}/bin/pw-link virtual_surround_sink:monitor_FL route_front_capture:input_FL 2>/dev/null || true
|
||||
${pkgs.pipewire}/bin/pw-link virtual_surround_sink:monitor_FR route_front_capture:input_FR 2>/dev/null || true
|
||||
${pkgs.pipewire}/bin/pw-link virtual_surround_sink:monitor_RL route_rear_capture:input_RL 2>/dev/null || true
|
||||
${pkgs.pipewire}/bin/pw-link virtual_surround_sink:monitor_RR route_rear_capture:input_RR 2>/dev/null || true
|
||||
${pkgs.pipewire}/bin/pw-link virtual_surround_sink:monitor_LFE route_lfe_capture:input_LFE 2>/dev/null || true
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
systemd.user.services.pipewire-surround-link-check = {
|
||||
description = "Check and fix surround sink links";
|
||||
after = [ "pipewire.service" "wireplumber.service" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = pkgs.writeShellScript "surround-link-check" ''
|
||||
if ${pkgs.pipewire}/bin/pw-cli ls Node 2>/dev/null | grep -q "bluez_output.F4_4E_FD_FB_58_62"; then
|
||||
if ${pkgs.pipewire}/bin/pw-link -l 2>/dev/null | grep -q "route_front_capture:input_FL.*alsa_input"; then
|
||||
${pkgs.systemd}/bin/systemctl --user start pipewire-surround-link.service
|
||||
fi
|
||||
if ! ${pkgs.pipewire}/bin/pw-link -l 2>/dev/null | grep -q "virtual_surround_sink:monitor_FL.*route_front_capture"; then
|
||||
${pkgs.systemd}/bin/systemctl --user start pipewire-surround-link.service
|
||||
fi
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
systemd.user.timers.pipewire-surround-link-check = {
|
||||
description = "Periodically check surround sink links";
|
||||
wantedBy = [ "default.target" ];
|
||||
timerConfig = {
|
||||
OnStartupSec = "10s";
|
||||
OnUnitActiveSec = "10s";
|
||||
Unit = "pipewire-surround-link-check.service";
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user