Add comprehensive PipeWire audio configuration with noise suppression

- Add modules/sound/pipewire.nix with full PipeWire stack
- Include RNNoise AI-powered noise suppression
- Add EasyEffects with pre-configured presets for mic and speakers
- Include multiple GUI applications (pavucontrol, helvum, qpwgraph, pwvucontrol)
- Add helper scripts: audio-setup, microphone-test, validate-audio
- Optimize for low-latency real-time audio processing
- Enable auto-start and desktop integration
- Remove duplicate PipeWire configs from hardware-co.nix and users/common.nix
- Import sound module through desktop/common.nix for all desktop machines
This commit is contained in:
Geir Okkenhaug Jerstad 2025-06-18 20:57:39 +02:00
parent 54e80f5c13
commit 9c9dcdc196
8 changed files with 1416 additions and 13 deletions

View file

@ -3,6 +3,10 @@
pkgs,
...
}: {
imports = [
../sound/pipewire.nix
];
# Common desktop configuration shared across all environments
# XDG Portal configuration for Wayland/X11 compatibility

273
modules/sound/README.md Normal file
View file

@ -0,0 +1,273 @@
# PipeWire with WirePlumber Configuration
This module provides a comprehensive PipeWire setup with WirePlumber session management, noise suppression, and GUI tools for audio management.
## Features
### Core Audio Stack
- **PipeWire**: Modern audio server with low latency
- **WirePlumber**: Session manager for device management and routing
- **ALSA/PulseAudio/JACK Compatibility**: Works with all major audio APIs
- **Real-time Processing**: RTKit integration for optimal performance
### Noise Suppression
- **RNNoise Plugin**: AI-powered noise suppression for microphones
- **EasyEffects Integration**: GUI for managing audio effects
- **Automatic Filter Chain**: Pre-configured noise suppression pipeline
### GUI Applications Included
- **EasyEffects**: Modern audio effects processor with noise suppression
- **PulseAudio Volume Control (pavucontrol)**: Volume and device management
- **Helvum**: Graphical PipeWire patchbay for routing
- **qpwgraph**: Qt-based PipeWire graph manager
- **pwvucontrol**: Native PipeWire volume control
## Quick Start
### 1. Import the Module
Add to your NixOS configuration:
```nix
imports = [
./modules/sound/pipewire.nix
];
```
### 2. Rebuild System
```bash
sudo nixos-rebuild switch
```
### 3. Verify Installation
```bash
# Check if PipeWire is running
systemctl --user status pipewire
# Launch the audio setup helper
audio-setup
# Test microphone with noise suppression
microphone-test
```
## Using Noise Suppression
### Method 1: EasyEffects (Recommended)
1. Launch EasyEffects: `easyeffects` or use the application menu
2. Go to the "Input" tab
3. Load the pre-configured "Microphone_Noise_Suppression" preset
4. Enable the RNNoise effect
5. Adjust the "VAD Threshold" (Voice Activity Detection) as needed
### Method 2: PipeWire Filter Chain (Automatic)
The configuration includes an automatic RNNoise filter chain that creates a "Noise Canceling Source" device. This appears as a separate microphone input in audio applications.
## GUI Applications Usage
### EasyEffects
- **Purpose**: Real-time audio effects and noise suppression
- **Launch**: `easyeffects` or from application menu
- **Features**: RNNoise, equalizer, compressor, limiter, gate
- **Auto-start**: Configured to start with desktop session
### Volume Controls
- **pavucontrol**: Traditional PulseAudio-style interface
- **pwvucontrol**: Native PipeWire interface
- **Usage**: Control volumes, switch devices, manage streams
### Audio Routing
- **Helvum**: Visual patchbay for connecting audio streams
- **qpwgraph**: Advanced graph-based routing interface
- **Usage**: Route audio between applications and devices
## Command-Line Tools
### System Status
```bash
# PipeWire status overview
wpctl status
# Real-time monitoring
pw-top
# Inspect audio objects
pw-dump | jq '.'
# Show metadata
pw-metadata
```
### Device Management
```bash
# List devices
wpctl status
# Set default sink
wpctl set-default SINK_ID
# Set volume
wpctl set-volume SOURCE_ID 80%
# Mute/unmute
wpctl set-mute SOURCE_ID toggle
```
### Testing
```bash
# Test microphone
microphone-test
# Record and playback test
arecord -d 5 -f cd test.wav && aplay test.wav
```
## Configuration Details
### Audio Quality Settings
- **Sample Rate**: 48kHz (professional audio standard)
- **Buffer Size**: 1024 samples (balanced latency/stability)
- **Resampling Quality**: High (level 4)
- **Channels**: Stereo support with spatial audio capabilities
### Noise Suppression Settings
- **RNNoise VAD Threshold**: 50% (adjustable)
- **VAD Grace Period**: 200ms
- **Noise Reduction**: 80% wet signal
- **Processing**: Real-time with minimal latency
### Performance Optimizations
- **Real-time Scheduling**: RTKit enabled
- **Memory Locking**: Enabled for critical processes
- **CPU Affinity**: Configurable per audio thread
- **Quantum Settings**: Optimized for low latency
## Troubleshooting
### Common Issues
#### No Audio Output
```bash
# Check PipeWire is running
systemctl --user restart pipewire pipewire-pulse wireplumber
# Check default devices
wpctl status
```
#### Microphone Not Working
```bash
# Test microphone detection
arecord -l
# Check permissions
groups $USER | grep audio
```
#### High CPU Usage
```bash
# Monitor PipeWire performance
pw-top
# Check buffer settings
pw-metadata | grep quantum
```
#### Noise Suppression Not Working
1. Verify RNNoise plugin is loaded: `ladspa-ls | grep -i noise`
2. Check EasyEffects preset is loaded
3. Ensure correct input device is selected
4. Adjust VAD threshold in EasyEffects
### Reset Configuration
```bash
# Reset user PipeWire configuration
rm -rf ~/.config/pipewire ~/.config/easyeffects
systemctl --user restart pipewire pipewire-pulse wireplumber
```
## Advanced Configuration
### Custom Filter Chains
Edit `/etc/pipewire/pipewire.conf.d/10-noise-suppression.conf` to modify the RNNoise filter chain parameters.
### Device-Specific Settings
Add rules to `/etc/wireplumber/wireplumber.conf.d/51-noise-suppression.conf` for specific audio devices.
### EasyEffects Presets
Custom presets are stored in `/etc/easyeffects/` and can be modified or extended.
## Integration with Applications
### Discord/Zoom/Teams
1. Set default microphone to "Noise Canceling Source" in application settings
2. Or use EasyEffects on the regular microphone input
3. Adjust noise gate and compressor settings as needed
### OBS Studio
1. Add "Application Audio Capture" source
2. Select the noise-suppressed microphone device
3. Or use OBS's built-in noise suppression with the processed audio
### Music Production (JACK)
```bash
# Start JACK mode if needed
pw-jack your-daw-application
```
## Updates and Maintenance
### Updating Configuration
After modifying the Nix configuration:
```bash
sudo nixos-rebuild switch
systemctl --user restart pipewire pipewire-pulse wireplumber
```
### Monitoring Performance
Regular checks recommended:
```bash
# Weekly performance check
pw-top
# Monthly configuration review
audio-setup
```
## See Also
- [PipeWire Documentation](https://docs.pipewire.org/)
- [WirePlumber Documentation](https://pipewire.pages.freedesktop.org/wireplumber/)
- [EasyEffects Documentation](https://github.com/wwmm/easyeffects)
- [RNNoise Project](https://jmvalin.ca/demo/rnnoise/)

View file

@ -0,0 +1,212 @@
{
config,
lib,
pkgs,
...
}: {
# Desktop entries for quick audio management access
environment.etc = {
# Desktop entry for EasyEffects
"xdg/autostart/easyeffects.desktop".text = ''
[Desktop Entry]
Name=EasyEffects
Comment=Audio effects for PipeWire applications
Icon=easyeffects
Exec=easyeffects --gapplication-service
Terminal=false
Type=Application
Categories=AudioVideo;Audio;
StartupNotify=true
X-GNOME-Autostart-enabled=true
'';
# Custom desktop entry for audio control center
"applications/audio-control-center.desktop".text = ''
[Desktop Entry]
Version=1.0
Type=Application
Name=Audio Control Center
Comment=Centralized audio management
Icon=audio-volume-high
Categories=AudioVideo;Audio;Settings;
Keywords=audio;sound;volume;pipewire;pulseaudio;
StartupNotify=true
Terminal=false
Exec=sh -c 'if command -v easyeffects >/dev/null 2>&1; then easyeffects; elif command -v pavucontrol >/dev/null 2>&1; then pavucontrol; elif command -v pwvucontrol >/dev/null 2>&1; then pwvucontrol; else helvum; fi'
'';
};
# Create a script for easy audio management
environment.systemPackages = with pkgs; [
(writeShellScriptBin "audio-setup" ''
#!/bin/bash
echo "🎵 Audio Control Center"
echo "======================"
echo ""
echo "Available audio applications:"
echo ""
if command -v easyeffects >/dev/null 2>&1; then
echo " 1. EasyEffects - Audio effects and noise suppression"
fi
if command -v pavucontrol >/dev/null 2>&1; then
echo " 2. PulseAudio Volume Control - Volume and device management"
fi
if command -v pwvucontrol >/dev/null 2>&1; then
echo " 3. PipeWire Volume Control - Native PipeWire control"
fi
if command -v helvum >/dev/null 2>&1; then
echo " 4. Helvum - PipeWire patchbay"
fi
if command -v qpwgraph >/dev/null 2>&1; then
echo " 5. qpwgraph - Qt PipeWire graph manager"
fi
echo ""
echo " 🔧 Audio Tools:"
echo " pw-top - Monitor PipeWire performance"
echo " pw-dump - Inspect PipeWire objects"
echo " pw-metadata - View/set PipeWire metadata"
echo " wpctl - WirePlumber control utility"
echo ""
echo "Choose an option (1-5) or press Enter for EasyEffects:"
read -r choice
case $choice in
1|"")
if command -v easyeffects >/dev/null 2>&1; then
echo "Starting EasyEffects..."
easyeffects
else
echo "EasyEffects not found!"
fi
;;
2)
if command -v pavucontrol >/dev/null 2>&1; then
echo "Starting PulseAudio Volume Control..."
pavucontrol
else
echo "pavucontrol not found!"
fi
;;
3)
if command -v pwvucontrol >/dev/null 2>&1; then
echo "Starting PipeWire Volume Control..."
pwvucontrol
else
echo "pwvucontrol not found!"
fi
;;
4)
if command -v helvum >/dev/null 2>&1; then
echo "Starting Helvum..."
helvum
else
echo "Helvum not found!"
fi
;;
5)
if command -v qpwgraph >/dev/null 2>&1; then
echo "Starting qpwgraph..."
qpwgraph
else
echo "qpwgraph not found!"
fi
;;
*)
echo "Invalid choice"
;;
esac
'')
(writeShellScriptBin "microphone-test" ''
#!/bin/bash
echo "🎤 Microphone Test & Setup"
echo "=========================="
echo ""
# Check if PipeWire is running
if ! pgrep -x pipewire >/dev/null; then
echo " PipeWire is not running!"
exit 1
fi
echo " PipeWire is running"
# Check for RNNoise
if ls /nix/store/*/lib/ladspa/librnnoise_ladspa.so >/dev/null 2>&1; then
echo " RNNoise plugin is available"
else
echo " RNNoise plugin not found"
fi
# List audio sources
echo ""
echo "📺 Available audio sources:"
wpctl status | grep -A 20 "Audio Sources"
echo ""
echo "🔊 Available audio sinks:"
wpctl status | grep -A 20 "Audio Sinks"
echo ""
echo "Would you like to:"
echo " 1. Test microphone input"
echo " 2. Open EasyEffects for noise suppression setup"
echo " 3. Show detailed audio device information"
echo " 4. Monitor audio levels"
echo ""
read -p "Choose an option (1-4): " choice
case $choice in
1)
echo "Recording 5 seconds of audio for playback test..."
echo "Speak into your microphone now!"
if command -v arecord >/dev/null 2>&1 && command -v aplay >/dev/null 2>&1; then
arecord -d 5 -f cd /tmp/mic_test.wav && echo "Playing back recording..." && aplay /tmp/mic_test.wav
rm -f /tmp/mic_test.wav
else
echo " ALSA utilities not available"
fi
;;
2)
if command -v easyeffects >/dev/null 2>&1; then
echo "Opening EasyEffects..."
easyeffects &
else
echo " EasyEffects not found"
fi
;;
3)
echo ""
echo "🔍 Detailed audio information:"
echo ""
pw-dump | jq '.[] | select(.info.props."media.class" == "Audio/Source" or .info.props."media.class" == "Audio/Sink") | {id: .id, name: .info.props."node.name", description: .info.props."node.description", class: .info.props."media.class"}'
;;
4)
echo "Monitoring audio levels (Ctrl+C to stop)..."
if command -v pw-top >/dev/null 2>&1; then
pw-top
else
echo "Monitoring with wpctl..."
while true; do
clear
wpctl status
sleep 2
done
fi
;;
*)
echo "Invalid choice"
;;
esac
'')
];
}

View file

@ -0,0 +1,546 @@
{
config,
lib,
pkgs,
...
}: {
# Create EasyEffects configuration directory and presets
environment.etc = {
# Input preset for microphone noise suppression
"easyeffects/input/Microphone_Noise_Suppression.json".text = builtins.toJSON {
input = {
blocklist = [];
equalizer = {
balance = 0.0;
bypass = false;
input-gain = 0.0;
left = {
band0 = {
frequency = 29.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band1 = {
frequency = 59.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band2 = {
frequency = 119.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band3 = {
frequency = 237.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band4 = {
frequency = 474.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band5 = {
frequency = 947.0;
gain = 2.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band6 = {
frequency = 1889.0;
gain = 1.5;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band7 = {
frequency = 3770.0;
gain = 1.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band8 = {
frequency = 7523.0;
gain = 0.5;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band9 = {
frequency = 15011.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
};
mode = "IIR";
num-bands = 10;
output-gain = 0.0;
pitch-left = 0.0;
pitch-right = 0.0;
right = {
band0 = {
frequency = 29.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band1 = {
frequency = 59.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band2 = {
frequency = 119.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band3 = {
frequency = 237.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band4 = {
frequency = 474.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band5 = {
frequency = 947.0;
gain = 2.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band6 = {
frequency = 1889.0;
gain = 1.5;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band7 = {
frequency = 3770.0;
gain = 1.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band8 = {
frequency = 7523.0;
gain = 0.5;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band9 = {
frequency = 15011.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
};
split-channels = false;
};
filter = {
balance = 0.0;
bypass = false;
frequency = 2000.0;
gain = 0.0;
mode = "12dB/oct Highpass";
quality = 0.7071;
slope = "x1";
};
gate = {
attack = 20.0;
bypass = false;
curve-threshold = -24.0;
curve-zone = 2.0;
hpf-frequency = 10.0;
hpf-mode = "12dB/oct Highpass";
input-gain = 0.0;
knee = 2.5;
lpf-frequency = 20000.0;
lpf-mode = "12dB/oct Lowpass";
makeup = 0.0;
ratio = 2.0;
release = 250.0;
sidechain = {
lookahead = 0.0;
mode = "RMS";
preamp = 0.0;
reactivity = 10.0;
source = "Middle";
};
threshold = -18.0;
};
limiter = {
alr = false;
alr-attack = 5.0;
alr-knee = 0.0;
alr-release = 50.0;
attack = 5.0;
bypass = false;
dithering = "None";
external-sidechain = false;
gain-boost = true;
input-gain = 0.0;
lookahead = 5.0;
mode = "Herm Thin";
output-gain = 0.0;
oversampling = "None";
release = 5.0;
sidechain-preamp = 0.0;
stereo-link = 100.0;
threshold = 0.0;
};
plugins_order = [
"filter"
"gate"
"equalizer"
"rnnoise"
"limiter"
];
rnnoise = {
bypass = false;
enable-vad = true;
input-gain = 0.0;
model-path = "";
output-gain = 0.0;
release = 20.0;
vad-thres = 50.0;
wet = 80.0;
};
};
};
# Output preset for speakers/headphones
"easyeffects/output/Speakers_Enhanced.json".text = builtins.toJSON {
output = {
blocklist = [];
equalizer = {
balance = 0.0;
bypass = false;
input-gain = 0.0;
left = {
band0 = {
frequency = 29.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band1 = {
frequency = 59.0;
gain = 1.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band2 = {
frequency = 119.0;
gain = 0.5;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band3 = {
frequency = 237.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band4 = {
frequency = 474.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band5 = {
frequency = 947.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band6 = {
frequency = 1889.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band7 = {
frequency = 3770.0;
gain = 1.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band8 = {
frequency = 7523.0;
gain = 2.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band9 = {
frequency = 15011.0;
gain = 1.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
};
mode = "IIR";
num-bands = 10;
output-gain = 0.0;
pitch-left = 0.0;
pitch-right = 0.0;
right = {
band0 = {
frequency = 29.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band1 = {
frequency = 59.0;
gain = 1.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band2 = {
frequency = 119.0;
gain = 0.5;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band3 = {
frequency = 237.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band4 = {
frequency = 474.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band5 = {
frequency = 947.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band6 = {
frequency = 1889.0;
gain = 0.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band7 = {
frequency = 3770.0;
gain = 1.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band8 = {
frequency = 7523.0;
gain = 2.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
band9 = {
frequency = 15011.0;
gain = 1.0;
mode = "RLC (BT)";
mute = false;
q = 4.36;
slope = "x1";
solo = false;
type = "Bell";
};
};
split-channels = false;
};
limiter = {
alr = false;
alr-attack = 5.0;
alr-knee = 0.0;
alr-release = 50.0;
attack = 5.0;
bypass = false;
dithering = "None";
external-sidechain = false;
gain-boost = true;
input-gain = 0.0;
lookahead = 5.0;
mode = "Herm Thin";
output-gain = 0.0;
oversampling = "None";
release = 5.0;
sidechain-preamp = 0.0;
stereo-link = 100.0;
threshold = -1.0;
};
plugins_order = [
"equalizer"
"limiter"
];
};
};
};
}

177
modules/sound/pipewire.nix Normal file
View file

@ -0,0 +1,177 @@
{
config,
lib,
pkgs,
...
}: {
imports = [
./easyeffects-presets.nix
./audio-desktop-integration.nix
];
# Enable PipeWire with full audio stack
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
jack.enable = true;
# Enable WirePlumber session manager
wireplumber.enable = true;
# Add noise suppression and audio processing packages
extraPackages = with pkgs; [
rnnoise-plugin # RNNoise noise suppression
easyeffects # Modern audio effects and filters
];
};
# Install audio management and GUI applications
environment.systemPackages = with pkgs; [
# Audio control and monitoring
pavucontrol # PulseAudio volume control (works with PipeWire)
helvum # Graphical patchbay for PipeWire
qpwgraph # Qt-based PipeWire graph manager
easyeffects # Audio effects and noise suppression GUI
pwvucontrol # Native PipeWire volume control
# Audio utilities
wireplumber # WirePlumber session manager
pipewire-pulse # PulseAudio compatibility
pipecontrol # PipeWire control utility
alsa-utils # ALSA utilities for testing
# Validation script
(writeShellScriptBin "validate-audio" (builtins.readFile ./validate-audio.sh))
# Optional: Professional audio tools
# qjackctl # JACK control GUI (for JACK applications)
# carla # Audio plugin host
];
# System-wide PipeWire configuration
environment.etc = {
# Main PipeWire configuration
"pipewire/pipewire.conf.d/10-noise-suppression.conf".text = ''
context.properties = {
default.clock.rate = 48000
default.clock.quantum = 1024
default.clock.min-quantum = 32
default.clock.max-quantum = 2048
}
context.modules = [
{
name = libpipewire-module-filter-chain
args = {
node.description = "Noise Canceling Source"
media.name = "Noise Canceling Source"
filter.graph = {
nodes = [
{
type = ladspa
name = rnnoise
plugin = ${pkgs.rnnoise-plugin}/lib/ladspa/librnnoise_ladspa.so
label = noise_suppressor_stereo
control = {
"VAD Threshold (%)" = 50.0
"VAD Grace Period (ms)" = 200
"Retroactive VAD Grace (ms)" = 0
}
}
]
}
capture.props = {
node.name = "capture.rnnoise_source"
node.passive = true
audio.rate = 48000
}
playback.props = {
node.name = "rnnoise_source"
media.class = "Audio/Source"
audio.rate = 48000
}
}
}
]
'';
# WirePlumber configuration for noise suppression
"wireplumber/wireplumber.conf.d/51-noise-suppression.conf".text = ''
monitor.alsa.rules = [
{
matches = [
{
device.name = "~alsa_card.*"
}
]
actions = {
update-props = {
device.profile-set = "auto"
device.auto-profile = true
}
}
}
]
monitor.bluez.rules = [
{
matches = [
{
device.name = "~bluez_card.*"
}
]
actions = {
update-props = {
bluez5.auto-connect = [ "hfp_hf" "hsp_hs" "a2dp_sink" ]
bluez5.hw-volume = [ "hfp_hf" "hsp_hs" "a2dp_sink" ]
}
}
}
]
'';
# Audio quality and latency optimization
"pipewire/pipewire-pulse.conf.d/10-audio-quality.conf".text = ''
pulse.properties = {
pulse.min.req = 32/48000
pulse.default.req = 1024/48000
pulse.min.quantum = 32/48000
pulse.max.quantum = 2048/48000
}
stream.properties = {
node.latency = 1024/48000
resample.quality = 4
channelmix.normalize = false
channelmix.mix-lfe = false
session.suspend-timeout-seconds = 0
}
'';
};
# Enable real-time audio processing
security.rtkit.enable = true;
# Audio group for users
users.groups.audio = {};
# Set environment variables for better audio performance
environment.variables = {
# PipeWire environment variables
PIPEWIRE_LATENCY = "1024/48000";
# Ensure applications use PipeWire
PULSE_RUNTIME_PATH = "/run/user/$UID/pulse";
};
# Enable additional audio-related services
services = {
# Enable udev rules for audio devices
udev.packages = with pkgs; [
alsa-utils
];
};
# User session configuration for audio
systemd.user.services.pipewire-pulse.wantedBy = ["default.target"];
}

204
modules/sound/validate-audio.sh Executable file
View file

@ -0,0 +1,204 @@
#!/usr/bin/env bash
# Audio Configuration Validation Script
# This script helps validate that PipeWire with noise suppression is working correctly
set -euo pipefail
echo "🎵 PipeWire Audio Configuration Validator"
echo "========================================"
echo ""
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
success() {
echo -e "${GREEN}$1${NC}"
}
warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
error() {
echo -e "${RED}$1${NC}"
}
info() {
echo -e "${BLUE} $1${NC}"
}
# Check if PipeWire is running
echo "1. Checking PipeWire service status..."
if systemctl --user is-active pipewire >/dev/null 2>&1; then
success "PipeWire service is running"
else
error "PipeWire service is not running"
echo " Try: systemctl --user start pipewire"
exit 1
fi
# Check WirePlumber
echo ""
echo "2. Checking WirePlumber session manager..."
if systemctl --user is-active wireplumber >/dev/null 2>&1; then
success "WirePlumber is running"
else
warning "WirePlumber is not running"
echo " Try: systemctl --user start wireplumber"
fi
# Check PipeWire-Pulse
echo ""
echo "3. Checking PipeWire-Pulse compatibility..."
if systemctl --user is-active pipewire-pulse >/dev/null 2>&1; then
success "PipeWire-Pulse is running"
else
warning "PipeWire-Pulse is not running"
echo " Try: systemctl --user start pipewire-pulse"
fi
# Check for RNNoise plugin
echo ""
echo "4. Checking for RNNoise noise suppression plugin..."
if find /nix/store -name "librnnoise_ladspa.so" 2>/dev/null | head -1 | grep -q .; then
success "RNNoise plugin found"
RNNOISE_PATH=$(find /nix/store -name "librnnoise_ladspa.so" 2>/dev/null | head -1)
info "Located at: $RNNOISE_PATH"
else
error "RNNoise plugin not found"
echo " This might indicate the package is not installed correctly"
fi
# Check audio devices
echo ""
echo "5. Checking available audio devices..."
if command -v wpctl >/dev/null 2>&1; then
SOURCES=$(wpctl status | grep -A 10 "Audio Sources" | grep -c "│" || echo "0")
SINKS=$(wpctl status | grep -A 10 "Audio Sinks" | grep -c "│" || echo "0")
if [ "$SOURCES" -gt 0 ]; then
success "Found $SOURCES audio source(s)"
else
warning "No audio sources found"
fi
if [ "$SINKS" -gt 0 ]; then
success "Found $SINKS audio sink(s)"
else
warning "No audio sinks found"
fi
else
error "wpctl command not found"
fi
# Check for GUI applications
echo ""
echo "6. Checking GUI audio applications..."
if command -v easyeffects >/dev/null 2>&1; then
success "EasyEffects available"
else
warning "EasyEffects not found"
fi
if command -v pavucontrol >/dev/null 2>&1; then
success "PulseAudio Volume Control available"
else
warning "pavucontrol not found"
fi
if command -v helvum >/dev/null 2>&1; then
success "Helvum patchbay available"
else
warning "Helvum not found"
fi
# Check configuration files
echo ""
echo "7. Checking configuration files..."
CONFIG_FILES=(
"/etc/pipewire/pipewire.conf.d/10-noise-suppression.conf"
"/etc/wireplumber/wireplumber.conf.d/51-noise-suppression.conf"
"/etc/pipewire/pipewire-pulse.conf.d/10-audio-quality.conf"
)
for config in "${CONFIG_FILES[@]}"; do
if [ -f "$config" ]; then
success "$(basename "$config") exists"
else
warning "$(basename "$config") not found"
fi
done
# Check for noise canceling source
echo ""
echo "8. Checking for noise canceling source..."
if command -v pw-dump >/dev/null 2>&1; then
if pw-dump | jq -r '.[] | select(.info.props."node.name" == "rnnoise_source") | .info.props."node.description"' 2>/dev/null | grep -q "Noise Canceling"; then
success "Noise Canceling Source device found"
else
warning "Noise Canceling Source device not found"
info "This is normal if no microphone is connected"
fi
else
warning "pw-dump not available, cannot check for noise canceling source"
fi
# Performance check
echo ""
echo "9. Checking audio performance..."
if command -v pw-top >/dev/null 2>&1; then
info "You can monitor real-time performance with: pw-top"
else
warning "pw-top not available for performance monitoring"
fi
# Summary
echo ""
echo "🎯 Quick Start Commands:"
echo "========================"
echo ""
echo "Start audio setup wizard: audio-setup"
echo "Test microphone: microphone-test"
echo "Launch EasyEffects: easyeffects"
echo "Control volumes: pavucontrol"
echo "Audio routing: helvum"
echo "Monitor performance: pw-top"
echo "Device status: wpctl status"
echo ""
# Check if user wants to run a test
echo "Would you like to run a quick microphone test? (y/N)"
read -r response
if [[ "$response" =~ ^[Yy]$ ]]; then
echo ""
info "Starting microphone test..."
if command -v microphone-test >/dev/null 2>&1; then
microphone-test
else
echo "Recording 3 seconds of audio..."
if command -v arecord >/dev/null 2>&1 && command -v aplay >/dev/null 2>&1; then
arecord -d 3 -f cd /tmp/audio_test.wav 2>/dev/null && \
echo "Playing back..." && \
aplay /tmp/audio_test.wav 2>/dev/null && \
rm -f /tmp/audio_test.wav
success "Microphone test completed"
else
error "Audio testing tools not available"
fi
fi
fi
echo ""
success "Audio configuration validation completed!"
echo ""
info "If you encounter issues, try:"
echo " • systemctl --user restart pipewire pipewire-pulse wireplumber"
echo " • Check the README.md for detailed troubleshooting"
echo " • Run 'audio-setup' for interactive configuration"

View file

@ -69,13 +69,6 @@
X11Forwarding = true; # For GUI applications over SSH
};
};
# Enable sound
pipewire = {
enable = true;
alsa.enable = true;
pulse.enable = true;
};
};
# XDG portal for desktop integration