|
#!/bin/bash |
|
|
|
################################################################################ |
|
# DeepFilterNet Installation Script for PipeWire |
|
# |
|
# This script installs DeepFilterNet LADSPA plugin system-wide and configures |
|
# it as a virtual microphone with noise cancellation in PipeWire. |
|
# |
|
# Usage: bash install-deepfilter.sh [path-to-libdeep_filter_ladspa.so] |
|
# |
|
# If no path is provided, the script will look for the .so file in: |
|
# - Current directory |
|
# - ~/Downloads |
|
# - Common download locations |
|
# |
|
# Author: Amin Nezampour |
|
# Date: 2025-11-06 |
|
################################################################################ |
|
|
|
set -euo pipefail |
|
|
|
RED='\033[0;31m' |
|
GREEN='\033[0;32m' |
|
YELLOW='\033[1;33m' |
|
BLUE='\033[0;34m' |
|
NC='\033[0m' |
|
|
|
# Configuration |
|
LADSPA_INSTALL_PATH="$HOME/.ladspa/libdeep_filter_ladspa.so" |
|
PIPEWIRE_CONFIG_DIR="$HOME/.config/pipewire/filter-chain.conf.d" |
|
PIPEWIRE_CONFIG_FILE="$PIPEWIRE_CONFIG_DIR/50-deepfilter-mic.conf" |
|
|
|
################################################################################ |
|
# Helper Functions |
|
################################################################################ |
|
|
|
print_header() { |
|
echo -e "${BLUE}=================================================================================${NC}" |
|
echo -e "${BLUE}$1${NC}" |
|
echo -e "${BLUE}=================================================================================${NC}" |
|
} |
|
|
|
print_success() { |
|
echo -e "${GREEN}✓ $1${NC}" |
|
} |
|
|
|
print_error() { |
|
echo -e "${RED}✗ $1${NC}" |
|
} |
|
|
|
print_info() { |
|
echo -e "${YELLOW}ℹ $1${NC}" |
|
} |
|
|
|
print_step() { |
|
echo -e "\n${BLUE}→ $1${NC}" |
|
} |
|
|
|
exit_error() { |
|
print_error "$1" |
|
exit 1 |
|
} |
|
|
|
################################################################################ |
|
# Requirement Checks |
|
################################################################################ |
|
|
|
check_requirements() { |
|
print_step "Checking system requirements..." |
|
|
|
if [[ "$OSTYPE" != "linux-gnu"* ]]; then |
|
exit_error "This script only works on Linux" |
|
fi |
|
print_success "Running on Linux" |
|
|
|
if command -v pactl >/dev/null 2>&1; then |
|
if ! pactl info 2>/dev/null | grep -qi "pipewire"; then |
|
exit_error "PipeWire does not appear to be the active audio server. 'pactl info' did not report PipeWire. Please install/configure PipeWire so it's the active audio server: |
|
- Debian/Ubuntu: sudo apt install pipewire pipewire-pulse wireplumber |
|
- Fedora: sudo dnf install pipewire pipewire-pulseaudio wireplumber |
|
- Arch: sudo pacman -S pipewire pipewire-pulse wireplumber" |
|
fi |
|
else |
|
if ! command -v wpctl >/dev/null 2>&1; then |
|
exit_error "Neither 'pactl' nor 'wpctl' were found. Please install PipeWire and its clients: |
|
- Debian/Ubuntu: sudo apt install pipewire pipewire-pulse wireplumber |
|
- Fedora: sudo dnf install pipewire pipewire-pulseaudio wireplumber |
|
- Arch: sudo pacman -S pipewire pipewire-pulse wireplumber" |
|
fi |
|
fi |
|
print_success "PipeWire is installed" |
|
|
|
if ! systemctl --user is-active --quiet pipewire; then |
|
print_info "PipeWire is not running. Attempting to start it..." |
|
systemctl --user start pipewire pipewire-pulse wireplumber || \ |
|
exit_error "Could not start PipeWire services" |
|
fi |
|
print_success "PipeWire is running" |
|
|
|
print_step "Checking for PipeWire filter-chain module..." |
|
|
|
if command -v shopt >/dev/null 2>&1; then |
|
shopt -s nullglob globstar || true |
|
fi |
|
|
|
local candidates=( |
|
/usr/lib/**/libpipewire-module-filter-chain.so |
|
/usr/lib64/**/libpipewire-module-filter-chain.so |
|
/lib/**/libpipewire-module-filter-chain.so |
|
/usr/lib/*/pipewire-*/libpipewire-module-filter-chain.so |
|
/usr/lib64/*/pipewire-*/libpipewire-module-filter-chain.so |
|
) |
|
|
|
local found="" |
|
for f in "${candidates[@]}"; do |
|
if [[ -f "$f" ]]; then |
|
found="$f" |
|
break |
|
fi |
|
done |
|
|
|
if [[ -z "$found" ]] && command -v ldconfig >/dev/null 2>&1; then |
|
found=$(ldconfig -p 2>/dev/null | awk '/libpipewire-module-filter-chain.so/ {print $NF; exit}') || true |
|
fi |
|
|
|
if [[ -z "$found" ]]; then |
|
exit_error "PipeWire filter-chain module not found. Install pipewire-plugins (or the package that provides libpipewire-module-filter-chain.so) and try again: |
|
- Debian/Ubuntu: sudo apt install pipewire-plugins |
|
- Fedora: sudo dnf install pipewire-plugins |
|
- Arch: sudo pacman -S pipewire" |
|
fi |
|
|
|
print_success "PipeWire filter-chain module found: $found" |
|
} |
|
|
|
################################################################################ |
|
# Find and Validate .so File |
|
################################################################################ |
|
|
|
find_libdeep_filter() { |
|
local provided_path="$1" |
|
|
|
if [[ -n "$provided_path" && -f "$provided_path" ]]; then |
|
echo "$provided_path" |
|
return 0 |
|
fi |
|
|
|
local search_paths=( |
|
"." |
|
"$HOME/Downloads" |
|
"$HOME/Downloads/libdeep_filter_ladspa*" |
|
"/tmp/libdeep_filter_ladspa*" |
|
) |
|
|
|
for path in "${search_paths[@]}"; do |
|
if [[ -f "$path" ]]; then |
|
echo "$path" |
|
return 0 |
|
fi |
|
for file in $path; do |
|
if [[ -f "$file" ]]; then |
|
echo "$file" |
|
return 0 |
|
fi |
|
done |
|
done |
|
|
|
return 1 |
|
} |
|
|
|
validate_so_file() { |
|
local so_file="$1" |
|
|
|
if [[ ! -f "$so_file" ]]; then |
|
exit_error "File not found: $so_file" |
|
fi |
|
|
|
if ! file "$so_file" | grep -q "ELF.*x86-64"; then |
|
exit_error "Invalid or incompatible binary: $so_file (expected x86-64 ELF)" |
|
fi |
|
print_success "Validated .so file: $so_file" |
|
|
|
print_step "Checking library dependencies..." |
|
if ldd "$so_file" | grep -q "not found"; then |
|
print_error "Missing dependencies detected:" |
|
ldd "$so_file" | grep "not found" |
|
exit_error "Please install missing libraries and try again" |
|
fi |
|
print_success "All dependencies are available" |
|
} |
|
|
|
################################################################################ |
|
# Installation |
|
################################################################################ |
|
|
|
install_ladspa_plugin() { |
|
local so_file="$1" |
|
|
|
print_step "Installing DeepFilter LADSPA plugin to user directory..." |
|
|
|
if [[ ! -d "$HOME/.ladspa" ]]; then |
|
print_info "Creating $HOME/.ladspa directory..." |
|
mkdir -p "$HOME/.ladspa" |
|
fi |
|
|
|
print_info "Copying $so_file to $LADSPA_INSTALL_PATH..." |
|
install -Dm644 "$so_file" "$LADSPA_INSTALL_PATH" || \ |
|
exit_error "Failed to install LADSPA plugin to $LADSPA_INSTALL_PATH" |
|
print_success "LADSPA plugin installed to $LADSPA_INSTALL_PATH" |
|
|
|
if [[ -f "$LADSPA_INSTALL_PATH" ]]; then |
|
print_success "Installation verified: $LADSPA_INSTALL_PATH" |
|
else |
|
exit_error "Installation verification failed" |
|
fi |
|
} |
|
|
|
################################################################################ |
|
# PipeWire Configuration |
|
################################################################################ |
|
|
|
create_pipewire_config() { |
|
print_step "Creating PipeWire filter-chain configuration..." |
|
|
|
mkdir -p "$PIPEWIRE_CONFIG_DIR" || \ |
|
exit_error "Failed to create config directory: $PIPEWIRE_CONFIG_DIR" |
|
|
|
cat > "$PIPEWIRE_CONFIG_FILE" << EOF |
|
# DeepFilterNet Noise Canceling Microphone |
|
# |
|
# This configuration creates a virtual microphone source that applies |
|
# real-time noise cancellation to your physical microphone input. |
|
# |
|
# The DeepFilter LADSPA plugin processes audio in mono, removing background noise |
|
# with an attenuation limit of 100 dB (maximum noise suppression). |
|
|
|
context.modules = [ |
|
{ name = libpipewire-module-filter-chain |
|
args = { |
|
node.description = "DeepFilter Microphone" |
|
media.name = "DeepFilter Microphone" |
|
|
|
# Define the audio processing filter graph |
|
filter.graph = { |
|
nodes = [ |
|
{ |
|
type = ladspa |
|
name = "DeepFilter Mono" |
|
plugin = $LADSPA_INSTALL_PATH |
|
label = deep_filter_mono |
|
control = { |
|
# Attenuation Limit in dB: |
|
# 100 = maximum noise suppression (default) |
|
# 24-18 = medium noise reduction |
|
# 12-6 = light noise reduction |
|
"Attenuation Limit (dB)" 100 |
|
} |
|
} |
|
] |
|
} |
|
|
|
# Audio configuration |
|
audio.rate = 48000 |
|
audio.channels = 1 |
|
audio.position = [ MONO ] |
|
|
|
# Capture properties: listen to the default system microphone |
|
capture.props = { |
|
node.name = "deep_filter_mic_input" |
|
node.passive = true |
|
# Optional: specify a particular microphone if auto-connect fails |
|
# node.target = "alsa_input.pci-0000_00_1f.3.analog-stereo" |
|
} |
|
|
|
# Playback properties: expose as a new virtual microphone to applications |
|
playback.props = { |
|
node.name = "deep_filter_mic_output" |
|
media.class = Audio/Source |
|
node.autoconnect = true |
|
} |
|
} |
|
} |
|
] |
|
EOF |
|
|
|
if [[ -f "$PIPEWIRE_CONFIG_FILE" ]]; then |
|
print_success "PipeWire configuration created: $PIPEWIRE_CONFIG_FILE" |
|
else |
|
exit_error "Failed to create PipeWire configuration" |
|
fi |
|
} |
|
|
|
################################################################################ |
|
# PipeWire Restart |
|
################################################################################ |
|
|
|
restart_pipewire() { |
|
print_step "Restarting PipeWire to load the configuration..." |
|
|
|
systemctl --user daemon-reload || \ |
|
exit_error "Failed to reload systemd user daemon" |
|
|
|
systemctl --user restart wireplumber pipewire pipewire-pulse || \ |
|
exit_error "Failed to restart PipeWire services" |
|
|
|
print_success "PipeWire restarted" |
|
print_info "Waiting for services to initialize (3 seconds)..." |
|
sleep 3 |
|
} |
|
|
|
################################################################################ |
|
# Verification |
|
################################################################################ |
|
|
|
verify_installation() { |
|
print_step "Verifying DeepFilter Microphone installation..." |
|
|
|
if [[ ! -f "$LADSPA_INSTALL_PATH" ]]; then |
|
print_error "LADSPA plugin not found at: $LADSPA_INSTALL_PATH" |
|
return 1 |
|
fi |
|
print_success "LADSPA plugin exists" |
|
|
|
if [[ ! -f "$PIPEWIRE_CONFIG_FILE" ]]; then |
|
print_error "PipeWire config not found at: $PIPEWIRE_CONFIG_FILE" |
|
return 1 |
|
fi |
|
print_success "PipeWire configuration exists" |
|
|
|
print_step "Checking if DeepFilter Microphone is available..." |
|
if wpctl status 2>/dev/null | grep -q "DeepFilter Microphone"; then |
|
print_success "DeepFilter Microphone is available in PipeWire" |
|
|
|
echo -e "\n${BLUE}Available audio sources:${NC}" |
|
wpctl status 2>/dev/null | sed -n '/Sources:/,/Source endpoints:/p' | head -20 |
|
return 0 |
|
else |
|
print_error "DeepFilter Microphone not found in PipeWire" |
|
print_info "Check PipeWire logs for errors:" |
|
print_info " journalctl --user -u pipewire -n 50" |
|
print_info " journalctl --user -u wireplumber -n 50" |
|
return 1 |
|
fi |
|
} |
|
|
|
################################################################################ |
|
# Usage Instructions |
|
################################################################################ |
|
|
|
print_usage_instructions() { |
|
print_header "DeepFilter Installation Complete!" |
|
|
|
cat << EOF |
|
|
|
The DeepFilter Microphone is now installed and available on your system. |
|
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
|
|
|
HOW TO USE: |
|
|
|
1. Set as Default Microphone (Optional): |
|
$ wpctl set-default <SOURCE_ID> |
|
|
|
Find the SOURCE_ID from: |
|
$ wpctl status |
|
|
|
2. Use in Applications: |
|
- Video conferencing (Zoom, Discord, Teams, etc.): Select "DeepFilter Microphone" |
|
- Sound settings: Choose "DeepFilter Microphone" as the input device |
|
- Command line recording: |
|
$ pw-cat --record --target "DeepFilter Microphone" output.wav |
|
|
|
3. Test the Configuration: |
|
$ pw-cat --record --target "DeepFilter Microphone" /tmp/dfn-test.wav |
|
$ pw-cat --playback /tmp/dfn-test.wav |
|
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
|
|
|
CONFIGURATION: |
|
|
|
Plugin Location: |
|
$LADSPA_INSTALL_PATH |
|
|
|
PipeWire Configuration: |
|
$PIPEWIRE_CONFIG_FILE |
|
|
|
To Adjust Noise Attenuation (in the config file): |
|
"Attenuation Limit (dB)" 100 (100 = max suppression, default) |
|
"Attenuation Limit (dB)" 24 (medium reduction) |
|
"Attenuation Limit (dB)" 12 (light reduction) |
|
|
|
After editing the config, restart PipeWire: |
|
$ systemctl --user restart wireplumber pipewire pipewire-pulse |
|
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
|
|
|
TROUBLESHOOTING: |
|
|
|
1. DeepFilter Microphone Not Appearing: |
|
$ journalctl --user -u pipewire -n 50 |
|
$ journalctl --user -u wireplumber -n 50 |
|
|
|
2. Silent or No Audio: |
|
- Check if the real microphone is working: |
|
$ pw-cat --record /tmp/test.wav |
|
- Ensure PipeWire routes correctly: |
|
$ pw-cli ls Node | grep -i deepfilter |
|
|
|
3. If You Need to Specify Your Microphone: |
|
Edit the config file and uncomment/modify the capture node.target property. |
|
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
|
|
|
EOF |
|
} |
|
|
|
################################################################################ |
|
# Main Execution |
|
################################################################################ |
|
|
|
main() { |
|
print_header "DeepFilterNet Installation for PipeWire" |
|
|
|
SO_FILE="${1:-}" |
|
|
|
check_requirements |
|
|
|
print_step "Locating DeepFilter LADSPA plugin..." |
|
if SO_FILE=$(find_libdeep_filter "$SO_FILE"); then |
|
print_success "Found: $SO_FILE" |
|
else |
|
exit_error "Could not find libdeep_filter_ladspa.so |
|
|
|
Please provide the path as an argument: |
|
bash $0 /path/to/libdeep_filter_ladspa.so |
|
|
|
Or place it in one of these locations: |
|
- Current directory |
|
- $HOME/Downloads |
|
- /tmp" |
|
fi |
|
|
|
validate_so_file "$SO_FILE" |
|
|
|
install_ladspa_plugin "$SO_FILE" |
|
|
|
create_pipewire_config |
|
|
|
restart_pipewire |
|
|
|
if verify_installation; then |
|
print_usage_instructions |
|
print_success "Installation completed successfully!" |
|
exit 0 |
|
else |
|
print_error "Installation completed but verification failed" |
|
print_info "Please check the logs and try restarting PipeWire manually: |
|
systemctl --user restart wireplumber pipewire pipewire-pulse" |
|
exit 1 |
|
fi |
|
} |
|
|
|
main "$@" |