feat: audio cast

This commit is contained in:
2026-06-09 14:39:32 +02:00
parent cf2108ad82
commit 5f6fbdc0ac
12 changed files with 241 additions and 16 deletions
+157
View File
@@ -0,0 +1,157 @@
{
pkgs,
lib,
config,
username,
...
}:
with lib;
let
cfg = config.hardware.audio.audio-share;
in
{
options.hardware.audio.audio-share = {
enable = mkEnableOption "LAN Audio Sharing";
partner = mkOption {
type = types.submodule {
options = {
ip = mkOption {
type = types.str;
description = "The IP address of the partner machine to share audio with.";
};
name = mkOption {
type = types.str;
description = "Name of the partner machine (for display purposes)";
};
combine = mkOption {
type = types.str;
description = "Name of the sink to combine the shared output with";
};
};
};
};
};
config =
let
sourcePort = 10001;
repairPort = 10002;
controlPort = 10003;
sinkName = "combined_output_3";
in
mkIf cfg.enable {
networking.firewall.allowedUDPPorts = [
sourcePort
repairPort
controlPort
];
home-manager.users.${username} = {
programs.waybar.settings.mainBar = {
"pulseaudio" = {
format-icons.${sinkName} = "󱝉";
on-click-right = lib.getExe (
pkgs.writeShellApplication {
name = "toggle-audio-share";
runtimeEnv = {
SINK_A = sinkName;
SINK_B = cfg.partner.combine;
};
text = builtins.readFile ./audio-share.sh;
}
);
};
};
xdg.configFile = {
"pipewire/pipewire.conf.d/52-audio-share.conf".text = builtins.toJSON {
"context.modules" = [
{
name = "libpipewire-module-roc-source";
args = {
"local.ip" = "0.0.0.0";
"roc.resampler.backend" = "default";
"roc.resampler.profile" = "default";
"roc.latency-tuner.backend" = "default";
"roc.latency-tuner.profile" = "default";
"fec.code" = "default";
"sess.latency.msec" = 100;
"local.source.port" = sourcePort;
"local.repair.port" = repairPort;
"local.control.port" = controlPort;
};
}
];
};
"pipewire/pipewire.conf.d/52-audio-share-sink.conf".text = builtins.toJSON {
"context.modules" = [
{
name = "libpipewire-module-roc-sink";
args = {
"fec.code" = "default";
"remote.ip" = cfg.partner.ip;
"remote.source.port" = sourcePort;
"remote.repair.port" = repairPort;
"remote.control.port" = controlPort;
"sink.props" = {
"node.name" = cfg.partner.name;
"node.description" = cfg.partner.name;
};
};
}
{
name = "libpipewire-module-combine-stream";
args = {
"combine.mode" = "sink";
"node.name" = sinkName;
"node.description" = "Shared Audio Output";
"combine.latency-compensate" = true;
"combine.props" = {
audio.position = [
"FL"
"FR"
];
};
"stream.rules" =
let
actions = {
create-stream = {
"combine.audio.position" = [
"FL"
"FR"
];
"audio.position" = [
"FL"
"FR"
];
};
};
in
[
{
matches = [
{
"media.class" = "Audio/Sink";
"node.name" = cfg.partner.combine;
}
];
inherit actions;
}
{
matches = [
{
"media.class" = "Audio/Sink";
"node.name" = cfg.partner.name;
}
];
inherit actions;
}
];
};
}
];
};
};
};
};
}