diff --git a/modules/common/services/audio.nix b/modules/common/services/audio.nix index fa7a88401..11a409052 100644 --- a/modules/common/services/audio.nix +++ b/modules/common/services/audio.nix @@ -33,6 +33,7 @@ in services.pipewire = { enable = true; pulse.enable = true; + alsa.enable = config.ghaf.development.debug.tools.enable; systemWide = true; extraConfig = { pipewire."10-remote-simple" = { @@ -43,62 +44,37 @@ in # Enable TCP socket for VMs pulseaudio clients "server.address" = [ { - address = "tcp:4713"; + address = "tcp:${toString cfg.pulseaudioTcpPort}"; "client.access" = "unrestricted"; } ]; - "pulse.min.req" = "128/48000"; # 2.7ms - "pulse.default.req" = "960/48000"; # 20 milliseconds - "pulse.min.frag" = "128/48000"; # 2.7ms - "pulse.default.frag" = "512/48000"; # ~10 ms - "pulse.default.tlength" = "512/48000"; # ~10 ms - "pulse.min.quantum" = "128/48000"; # 2.7ms + "pulse.min.req" = "1024/48000"; + "pulse.min.quantum" = "1024/48000"; + "pulse.idle.timeout" = "3"; }; } ]; }; }; + # Disable the auto-switching to the low-quality HSP profile + wireplumber.extraConfig.disable-autoswitch = { + "wireplumber.settings" = { + "bluetooth.autoswitch-to-headset-profile" = "false"; + }; + }; }; - hardware.pulseaudio.extraConfig = '' - # Set sink and source default max volume to about 75% (0-65536) - set-sink-volume @DEFAULT_SINK@ 48000 - set-source-volume @DEFAULT_SOURCE@ 48000 - ''; - # Allow ghaf user to access pulseaudio and pipewire users.extraUsers.ghaf.extraGroups = [ "audio" "video" - "pulse-access" "pipewire" ]; - # Dummy service to get pipewire and pulseaudio services started at boot - # Normally Pipewire and pulseaudio are started when they are needed by user, - # We don't have users in audiovm so we need to give PW/PA a slight kick.. - # This calls pulseaudios pa-info binary to get information about pulseaudio current - # state which starts pipewire-pulseaudio service in the process. - systemd.services.pulseaudio-starter = { - after = [ - "pipewire.service" - "network-online.target" - ]; - requires = [ - "pipewire.service" - "network-online.target" - ]; - wantedBy = [ "default.target" ]; - path = [ pkgs.coreutils ]; - enable = true; - serviceConfig = { - User = "ghaf"; - Group = "ghaf"; - }; - script = ''${pkgs.pulseaudio}/bin/pa-info > /dev/null 2>&1''; - }; + # Start pipewire on system boot + systemd.services.pipewire.wantedBy = [ "multi-user.target" ]; - # Open TCP port for the PDF XDG socket + # Open TCP port for the pipewire pulseaudio socket networking.firewall.allowedTCPPorts = [ cfg.pulseaudioTcpPort ]; }; } diff --git a/modules/reference/appvms/chromium.nix b/modules/reference/appvms/chromium.nix index 3f0f0125e..48ec7444e 100644 --- a/modules/reference/appvms/chromium.nix +++ b/modules/reference/appvms/chromium.nix @@ -32,11 +32,11 @@ in in [ pkgs.chromium - pkgs.pulseaudio pkgs.xdg-utils xdgPdfItem xdgOpenPdf - ]; + ] + ++ lib.optional config.ghaf.development.debug.tools.enable pkgs.alsa-utils; # TODO create a repository of mac addresses to avoid conflicts macAddress = "02:00:00:03:05:01"; ramMb = 3072; @@ -54,13 +54,10 @@ in hardware.pulseaudio = { enable = true; extraConfig = '' - load-module module-tunnel-sink sink_name=chromium-speaker server=audio-vm:4713 format=s16le channels=2 rate=48000 - load-module module-tunnel-source source_name=chromium-mic server=audio-vm:4713 format=s16le channels=1 rate=48000 - - # Set sink and source default max volume to about 90% (0-65536) - set-sink-volume chromium-speaker 60000 - set-source-volume chromium-mic 60000 + load-module module-tunnel-sink-new sink_name=chromium-speaker server=audio-vm:4713 reconnect_interval_ms=1000 + load-module module-tunnel-source-new source_name=chromium-mic server=audio-vm:4713 reconnect_interval_ms=1000 ''; + package = pkgs.pulseaudio-ghaf; }; time.timeZone = config.time.timeZone; diff --git a/overlays/custom-packages/default.nix b/overlays/custom-packages/default.nix index c81697a64..c2d90be84 100644 --- a/overlays/custom-packages/default.nix +++ b/overlays/custom-packages/default.nix @@ -20,4 +20,5 @@ mitmweb-ui = final.callPackage ../../packages/mitmweb-ui { }; gtklock = import ./gtklock { inherit prev; }; hardware-scan = final.callPackage ../../packages/hardware-scan { }; + pulseaudio-ghaf = import ./pulseaudio { inherit prev; }; }) diff --git a/overlays/custom-packages/pulseaudio/default.nix b/overlays/custom-packages/pulseaudio/default.nix new file mode 100644 index 000000000..65338250b --- /dev/null +++ b/overlays/custom-packages/pulseaudio/default.nix @@ -0,0 +1,7 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ prev, ... }: +prev.pulseaudio.overrideAttrs (_prevAttrs: { + # This patch enables the live switching of pulse tunnels to different sinks + patches = _prevAttrs.patches ++ [ ./pulseaudio-remove-dont-move.patch ]; +}) diff --git a/overlays/custom-packages/pulseaudio/pulseaudio-remove-dont-move.patch b/overlays/custom-packages/pulseaudio/pulseaudio-remove-dont-move.patch new file mode 100644 index 000000000..69680833a --- /dev/null +++ b/overlays/custom-packages/pulseaudio/pulseaudio-remove-dont-move.patch @@ -0,0 +1,26 @@ +diff --git a/src/modules/module-tunnel-sink-new.c b/src/modules/module-tunnel-sink-new.c +index 0b91ce266..ea41c50e2 100644 +--- a/src/modules/module-tunnel-sink-new.c ++++ b/src/modules/module-tunnel-sink-new.c +@@ -417,7 +417,7 @@ static void on_sink_created(struct userdata *u) { + if (pa_stream_connect_playback(u->stream, + u->remote_sink_name, + &bufferattr, +- PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_DONT_MOVE | PA_STREAM_START_CORKED | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY, ++ PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_START_CORKED | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY, + NULL, + NULL) < 0) { + pa_log_error("Could not connect stream."); +diff --git a/src/modules/module-tunnel-source-new.c b/src/modules/module-tunnel-source-new.c +index d75fe9e6b..510d0c1aa 100644 +--- a/src/modules/module-tunnel-source-new.c ++++ b/src/modules/module-tunnel-source-new.c +@@ -397,7 +397,7 @@ static void on_source_created(struct userdata *u) { + if (pa_stream_connect_record(u->stream, + u->remote_source_name, + &bufferattr, +- PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_DONT_MOVE|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY) < 0) { ++ PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY) < 0) { + pa_log_debug("Could not create stream: %s", pa_strerror(pa_context_errno(u->context))); + u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP); + } diff --git a/packages/bt-launcher/default.nix b/packages/bt-launcher/default.nix index 528302663..08e2fdb94 100644 --- a/packages/bt-launcher/default.nix +++ b/packages/bt-launcher/default.nix @@ -11,6 +11,7 @@ writeShellApplication { name = "bt-launcher"; text = '' + export PULSE_SERVER=audio-vm:4713 export DBUS_SESSION_BUS_ADDRESS=unix:path=/tmp/ssh_session_dbus.sock export DBUS_SYSTEM_BUS_ADDRESS=unix:path=/tmp/ssh_system_dbus.sock ${openssh}/bin/ssh -M -S /tmp/control_socket \