mirror of
https://github.com/zephrynis/nix-flake.git
synced 2026-08-17 21:45:53 +00:00
Add a systemd user service (spotify-duck.py) that meters Discord's output stream and the denoised rnnoise mic via pw-record and lowers Spotify's volume via wpctl while anyone is speaking, restoring it a beat after silence. Tuning knobs are exposed as service Environment entries. Ignore Python __pycache__. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
54 lines
2.4 KiB
Nix
54 lines
2.4 KiB
Nix
{ pkgs, ... }:
|
|
|
|
# Voice-activity ducking: while you OR someone else is speaking in a Discord
|
|
# (Vesktop) voice call, Spotify's volume is lowered; it returns to normal a
|
|
# beat after everyone goes quiet. Nothing else on the system is touched.
|
|
#
|
|
# The daemon (./spotify-duck.py) meters Discord's own output stream and your
|
|
# denoised mic (rnnoise_source, from modules/noise-suppression.nix) directly via
|
|
# `pw-record`, so it never reroutes audio and is independent of which output
|
|
# device is active. See that file's header for the full rationale. Tune the
|
|
# behaviour with the Environment entries below and `systemctl --user restart
|
|
# spotify-duck` (no rebuild needed to experiment; make it permanent here after).
|
|
|
|
let
|
|
spotify-duck = pkgs.writeShellApplication {
|
|
name = "spotify-duck";
|
|
# pw-record/pw-dump live in pipewire; wpctl in wireplumber; python3 to run it.
|
|
runtimeInputs = [ pkgs.python3 pkgs.pipewire pkgs.wireplumber ];
|
|
text = ''exec python3 ${./spotify-duck.py} "$@"'';
|
|
};
|
|
in
|
|
{
|
|
systemd.user.services.spotify-duck = {
|
|
Unit = {
|
|
Description = "Duck Spotify while speaking on Discord (voice-activity)";
|
|
After = [ "pipewire.service" "wireplumber.service" ];
|
|
};
|
|
|
|
Service = {
|
|
ExecStart = "${spotify-duck}/bin/spotify-duck";
|
|
Restart = "on-failure";
|
|
RestartSec = 3;
|
|
# Tuning knobs — override and `systemctl --user restart spotify-duck`.
|
|
Environment = [
|
|
"DUCK_LEVEL=0.35" # ducked volume = your current volume * this (35%)
|
|
"MIC_THRESHOLD=0.004" # you-speaking RMS gate (measured: speech 0.004-0.015,
|
|
# silence <0.0025 on rnnoise_source). Lower toward
|
|
# 0.003 if soft speech is missed; raise if it dips
|
|
# randomly.
|
|
"DISC_THRESHOLD=0.008" # others-speaking RMS gate on Discord's output
|
|
"RELEASE_MS=900" # restore this long after the last speech
|
|
"MIC_TARGET=rnnoise_source" # mic node; the denoised source you use in Discord
|
|
"DISCORD_APP=vesktop" # application.name of the Discord client
|
|
"SPOTIFY_MATCH=spotify" # substring identifying Spotify's stream
|
|
];
|
|
};
|
|
|
|
# default.target (not graphical-session.target) so it reliably starts on
|
|
# login regardless of how the Hyprland session activates targets; the daemon
|
|
# tolerates PipeWire not being up yet and self-heals.
|
|
Install.WantedBy = [ "default.target" ];
|
|
};
|
|
}
|