fix: getting rnnoise model

This commit is contained in:
Krzysztof kuhy Rudnicki 2025-10-12 18:57:55 +02:00
parent 06c8121c79
commit 158a1c7b01
4 changed files with 814 additions and 0 deletions

104
README_clean_audio.md Normal file
View File

@ -0,0 +1,104 @@
# clean_audio.sh — automatic speech cleaning (FFmpeg)
This script batchcleans noisy speech recordings with ffmpeg using simple, reliable filters tuned for ASR (e.g., fasterwhisper). By default it REQUIRES RNNoise (arnndn) and will try to autodiscover or download a model. You can optin to fallback filters with `--allow-fallback`.
## Install
- Required: ffmpeg. Most distros: `sudo pacman -S ffmpeg` or `sudo apt install ffmpeg`.
- Recommended: ffmpeg with `arnndn` filter and an RNNoise model file (e.g., from Mozilla RNNoise community models). The script will auto-detect common model locations or download one via `Bash/get_rnnoise_model.sh`. You can pass a specific model with `-m /path/to/model.nn`.
Make executable:
```bash
chmod +x Bash/clean_audio.sh
```
## Quick start
- Single file, default ASR preset (16k mono, denoise, highpass, limiter):
```bash
Bash/clean_audio.sh path/to/file.wav
```
This produces `path/to/file_clean.wav`.
- Whole folder, 4 parallel jobs, output to `cleaned/`:
```bash
Bash/clean_audio.sh path/to/folder -O cleaned -j 4
```
- Use an RNNoise model explicitly (if your ffmpeg has arnndn):
```bash
Bash/clean_audio.sh input.wav -m models/rnnoise_model.nn
```
If you omit `-m`, the script will look in common locations; if not found, it will attempt a download via `Bash/get_rnnoise_model.sh`.
Advanced options and compatibility:
- The cleaner requires RNNoise by default. To allow non-ML fallback filters (afftdn), add `--allow-fallback`.
- The script uses advanced filter settings when available (e.g., afftdn with `md`). If your ffmpeg build lacks these options, it will error with guidance. Add `--no-advanced` (or `--compat`) to avoid such params.
- Podcast preset (adds dynamics and loudness leveling):
```bash
Bash/clean_audio.sh input.wav --preset podcast
```
## Options
```text
Usage: clean_audio.sh <input-file|input-dir> [options]
Options:
-O, --out-dir DIR Output directory (default: alongside input file).
-e, --ext EXT Output extension/container: wav|flac (default: wav).
-m, --model PATH RNNoise model file for arnndn; falls back to afftdn if unavailable.
--no-ml Do not use arnndn even if model is provided; use afftdn.
--preset NAME asr (default) | podcast | aggressive
-j, --jobs N Parallel jobs for directory mode (default: 1).
-f, --force Overwrite outputs if they exist.
-q, --quiet Reduce ffmpeg logging noise.
--lowpass FREQ Optional low-pass cutoff (e.g., 8000). Disabled by default.
--suffix SUF Suffix for output basename (default: _clean).
```
## Designed for ASR (fasterwhisper)
Default output format is mono, 16 kHz, PCM 16bit WAV—ideal for most Whisper/fasterwhisper pipelines. You can feed the cleaned files directly into your transcription step.
If you prefer FLAC to save space without quality loss:
```bash
Bash/clean_audio.sh input.wav -e flac -O cleaned
```
## Presets
- asr (default): light, ASRfriendly cleanup; prevents clipping.
- podcast: adds gentle dynamics and approximate loudness normalization (singlepass `loudnorm`).
- aggressive: heavier gate/dynamics; can suppress background more, but may slightly hurt ASR accuracy—use sparingly.
## Tips
- If you see artifacts from RNNoise, try without a model (uses `afftdn`), or add a lowpass (e.g., `--lowpass 8000`).
- For extremely boomy bar recordings, raise highpass by editing `HIGHPASS` in the script or add `--lowpass`.
- If your ffmpeg lacks `arnndn`, you can install a newer build or keep the fallback (afftdn works fine for many cases).
- If your ffmpeg is missing features, you can use the helper:
```bash
chmod +x Bash/install_ffmpeg_with_arnndn.sh
Bash/install_ffmpeg_with_arnndn.sh
```
It will suggest distro options or build FFmpeg from source with `--enable-librnnoise`.
RNNoise model downloader helper:
```bash
chmod +x Bash/get_rnnoise_model.sh
Bash/get_rnnoise_model.sh --yes
```
This saves a model into `Bash/models/` which the cleaner will auto-discover.
## Troubleshooting
- “arnndn not available”: Your ffmpeg wasnt built with it. The script will use `afftdn` instead.
- Output sounds thin: lower the highpass (edit `HIGHPASS=80` in script to `60`) or remove lowpass.
- Level too low/high: choose the `podcast` preset for auto leveling, or add your own `loudnorm` in post.
## License
This helper script is provided under the repositorys LICENSE.

390
clean_audio.sh Executable file
View File

@ -0,0 +1,390 @@
#!/usr/bin/env bash
set -euo pipefail
# clean_audio.sh — Fully automatic audio cleaner for speech (ASR-friendly)
#
# - Default preset is tuned for ASR (faster-whisper):
# mono, 16 kHz, high-pass filter, denoise (RNNoise arnndn by default if model found/provided; else afftdn),
# peak limiting to -0.5 dBFS. No aggressive gating/compression by default.
# - Optional "podcast" preset adds gentle dynamics and loudness leveling.
# - Accepts single files or directories (recursively).
# - Optional parallel processing.
#
# Dependencies: ffmpeg (arnndn filter recommended for best results)
# Optional: an RNNoise model file for arnndn (auto-discovered if present; otherwise falls back to afftdn)
#
# Usage examples:
# Bash/clean_audio.sh input.wav # -> input_clean.wav (same folder)
# Bash/clean_audio.sh input.wav -O out_dir # -> out_dir/input_clean.wav
# Bash/clean_audio.sh input_dir -O cleaned/ -j 4 # -> processes all audio files in dir
# Bash/clean_audio.sh input.wav -m models/rn.nn # -> use RNNoise model
# Bash/clean_audio.sh input.wav --preset podcast # -> add dynamics leveler
#
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
print_usage() {
cat <<EOF
Usage: $0 <input-file|input-dir> [options]
Options:
-O, --out-dir DIR Output directory (default: alongside input file).
-e, --ext EXT Output extension/container: wav|flac (default: wav).
-m, --model PATH RNNoise model file for arnndn; required by default unless --allow-fallback.
--no-ml Do not use arnndn even if model is provided (requires --allow-fallback).
--preset NAME asr (default) | podcast | aggressive
-j, --jobs N Parallel jobs for directory mode (default: 1).
-f, --force Overwrite outputs if they exist (ffmpeg -y).
-q, --quiet Reduce ffmpeg logging noise.
--lowpass FREQ Optional low-pass cutoff (e.g., 8000). Disabled by default.
--suffix SUF Suffix for output basename (default: _clean).
-h, --help Show this help.
Notes:
- Default sample rate is 16 kHz mono PCM 16-bit (good for most speech ASR models).
- If arnndn (RNNoise) is used, it usually outperforms afftdn for speech denoise.
- The 'podcast' preset adds gentle dynamics and loudness normalization (single-pass).
EOF
}
require_cmd() {
command -v "$1" >/dev/null 2>&1 || {
echo "Error: Required command '$1' not found in PATH" >&2
exit 1
}
}
# Defaults
OUT_DIR=""
OUT_EXT="wav"
RN_MODEL=""
NO_ML=false
REQUIRE_ML=true # default: require RNNoise; install/guide if missing; fail fast if unavailable
PRESET="asr"
JOBS=1
FORCE=false
QUIET=false
LOWPASS=""
SUFFIX="_clean"
HIGHPASS="80"
AFFTDN_NF="-25" # noise floor in dB for afftdn
AFFTDN_MD="8" # mode for afftdn (higher can be more aggressive); requires builds that support 'md'
NO_ADVANCED=false # when true, avoid advanced options that some ffmpeg builds lack
# Parse args
if [[ $# -lt 1 ]]; then
print_usage
exit 1
fi
INPUT_PATH="$1"; shift || true
while [[ $# -gt 0 ]]; do
case "$1" in
-O|--out-dir)
OUT_DIR="$2"; shift 2;;
-e|--ext)
OUT_EXT="$2"; shift 2;;
-m|--model)
RN_MODEL="$2"; shift 2;;
--no-ml)
NO_ML=true; shift;;
--preset)
PRESET="$2"; shift 2;;
-j|--jobs)
JOBS="$2"; shift 2;;
-f|--force)
FORCE=true; shift;;
-q|--quiet)
QUIET=true; shift;;
--lowpass)
LOWPASS="$2"; shift 2;;
--suffix)
SUFFIX="$2"; shift 2;;
--no-advanced|--compat)
NO_ADVANCED=true; shift;;
--allow-fallback)
REQUIRE_ML=false; shift;;
-h|--help)
print_usage; exit 0;;
*)
echo "Unknown option: $1" >&2
print_usage
exit 1;;
esac
done
require_cmd ffmpeg
# Resolve FFmpeg binary (env override -> local build -> system)
FFMPEG_BIN=${FFMPEG_BIN:-}
if [[ -z "${FFMPEG_BIN}" ]]; then
if [[ -x "$SCRIPT_DIR/ffmpeg-build/FFmpeg/ffmpeg" ]]; then
FFMPEG_BIN="$SCRIPT_DIR/ffmpeg-build/FFmpeg/ffmpeg"
else
FFMPEG_BIN="ffmpeg"
fi
fi
if ! command -v "$FFMPEG_BIN" >/dev/null 2>&1 && [[ ! -x "$FFMPEG_BIN" ]]; then
echo "Error: FFmpeg binary not found: $FFMPEG_BIN" >&2
exit 1
fi
if ! $QUIET; then
echo "Using FFmpeg binary: $FFMPEG_BIN" >&2
fi
FFMPEG_LOG=(-hide_banner)
if $QUIET; then
FFMPEG_LOG+=( -loglevel error )
else
FFMPEG_LOG+=( -loglevel info )
fi
FFMPEG_OVERWRITE=(-n)
if $FORCE; then
FFMPEG_OVERWRITE=(-y)
fi
arnndn_available=false
if "$FFMPEG_BIN" -hide_banner -h filter=arnndn >/dev/null 2>&1; then
arnndn_available=true
else
if "$FFMPEG_BIN" -hide_banner -filters 2>/dev/null | grep -Eq '(^|[[:space:]])arnndn([[:space:]]|$)'; then
arnndn_available=true
fi
fi
if ! $QUIET; then
echo "arnndn_available=$arnndn_available" >&2
fi
# Check if afftdn supports 'md' option
afftdn_supports_md=false
if "$FFMPEG_BIN" -hide_banner -h filter=afftdn 2>/dev/null | grep -q " md="; then
afftdn_supports_md=true
fi
# Try to auto-discover an RNNoise model if none provided
find_default_rn_model() {
local candidate=""
# Allow env variable override
if [[ -n "${RNNOISE_MODEL:-}" && -f "${RNNOISE_MODEL}" ]]; then
echo "${RNNOISE_MODEL}"
return 0
fi
local dirs=(
"$SCRIPT_DIR/models"
"$SCRIPT_DIR/../models"
"/usr/share/rnnoise"
"/usr/local/share/rnnoise"
"/usr/share/ffmpeg/models"
"$HOME/.local/share/rnnoise"
)
# Prefer '.rnnn' models (rnnoise-nu style) over legacy '.nn'
local exts=("rnnn" "nn" "model")
for d in "${dirs[@]}"; do
if [[ -d "$d" ]]; then
for ext in "${exts[@]}"; do
# Pick the first matching model file
for f in "$d"/*."$ext"; do
if [[ -f "$f" ]]; then
echo "$f"
return 0
fi
done
done
fi
done
return 1
}
use_arnndn=false
if [[ $NO_ML == false ]]; then
if [[ $arnndn_available == false ]]; then
if $REQUIRE_ML; then
echo "Error: FFmpeg 'arnndn' filter not available. Please install/upgrade FFmpeg with librnnoise (see Bash/install_ffmpeg_with_arnndn.sh)." >&2
exit 9
fi
else
# arnndn available; require an external model
if [[ -n "$RN_MODEL" && -f "$RN_MODEL" ]]; then
:
else
if model_path=$(find_default_rn_model); then
RN_MODEL="$model_path"
else
if [[ -x "$SCRIPT_DIR/get_rnnoise_model.sh" ]]; then
RN_TARGET_DIR="$SCRIPT_DIR/models" RN_TARGET_NAME="rnnoise_model.rnnn" "$SCRIPT_DIR/get_rnnoise_model.sh" --yes || true
if model_path=$(find_default_rn_model); then
RN_MODEL="$model_path"
fi
fi
fi
fi
if [[ -z "$RN_MODEL" ]]; then
echo "Error: RNNoise model required but not found. Automatic download failed." >&2
echo "Hint: Set RN_URL to a reachable model URL and run Bash/get_rnnoise_model.sh, or supply -m /path/to/model.nn." >&2
exit 10
fi
use_arnndn=true
echo "Using RNNoise external model: $RN_MODEL" >&2
fi
fi
build_filters() {
local filters=()
# Remove low-frequency rumble typical for handheld/room noise
filters+=("highpass=f=${HIGHPASS}")
# Denoise
if $use_arnndn; then
# arnndn with full mix keeps the model output; if no external model, rely on built-in
filters+=("aresample=48000")
filters+=("arnndn=m=${RN_MODEL}:mix=1.0")
else
# afftdn: FFT-based denoise, tune nf (noise floor) as needed
if $REQUIRE_ML; then
echo "Error: RNNoise required but not in use; aborting rather than falling back to afftdn. Use --allow-fallback to permit." >&2
exit 11
fi
if $NO_ADVANCED; then
filters+=("afftdn=nf=${AFFTDN_NF}")
else
if $afftdn_supports_md; then
filters+=("afftdn=nf=${AFFTDN_NF}:md=${AFFTDN_MD}")
else
echo "Error: Your ffmpeg's afftdn filter does not support 'md='." >&2
echo "Hint: Install/upgrade ffmpeg to a build that supports afftdn md or rerun with --no-advanced." >&2
echo " On Debian/Ubuntu you may need a newer ffmpeg from a PPA or build from source." >&2
exit 8
fi
fi
fi
# Optional low-pass to shave hiss; keep disabled unless requested
if [[ -n "$LOWPASS" ]]; then
filters+=("lowpass=f=${LOWPASS}")
fi
case "$PRESET" in
asr)
# ASR-friendly: avoid heavy gating/leveling, just prevent clipping
filters+=("alimiter=limit=0.94")
;;
podcast)
# Gentle dynamic normalization and broadcast-ish loudness (single-pass)
# Note: single-pass loudnorm is approximate but OK for quick workflows
filters+=("dynaudnorm=f=500:g=5:p=0.1")
filters+=("loudnorm=i=-18:lra=9:tp=-2.0")
;;
aggressive)
# Heavier clean-up; may harm ASR slightly but suppress background more
filters+=("agate=threshold=0.012:ratio=2.5:release=200")
filters+=("dynaudnorm=f=400:g=7:p=0.1")
filters+=("loudnorm=i=-18:lra=9:tp=-2.0")
;;
*) ;;
esac
# Resample and format at the end for ASR
filters+=("aresample=16000")
filters+=("aformat=channel_layouts=mono:sample_fmts=s16")
local IFS=","; echo "${filters[*]}"
}
make_out_path_for_file() {
local in_file="$1"
local base
base=$(basename -- "$in_file")
base="${base%.*}"
local out_base="${base}${SUFFIX}.${OUT_EXT}"
if [[ -n "$OUT_DIR" ]]; then
mkdir -p -- "$OUT_DIR"
echo "$OUT_DIR/$out_base"
else
local dir
dir=$(dirname -- "$in_file")
echo "$dir/$out_base"
fi
}
process_one() {
local in_file="$1"
local out_file
out_file=$(make_out_path_for_file "$in_file")
# Choose codec based on extension
local codec=( -c:a pcm_s16le )
if [[ "$OUT_EXT" == "flac" ]]; then
codec=( -c:a flac )
fi
local af
af=$(build_filters)
if [[ -f "$out_file" && $FORCE == false ]]; then
echo "Skip (exists): $out_file"
return 0
fi
echo "Cleaning: $in_file -> $out_file"
"$FFMPEG_BIN" "${FFMPEG_LOG[@]}" "${FFMPEG_OVERWRITE[@]}" -i "$in_file" -af "$af" "${codec[@]}" "$out_file"
}
# Concurrency helpers (bash >= 5 supports wait -n; fallback to sequential if not)
supports_wait_n=false
if [[ -n "${BASH_VERSINFO:-}" && ${BASH_VERSINFO[0]} -ge 5 ]]; then
supports_wait_n=true
fi
run_dir() {
local dir="$1"
# Common audio extensions (case-insensitive)
mapfile -d '' files < <(find "$dir" -type f \
\( -iname "*.wav" -o -iname "*.mp3" -o -iname "*.m4a" -o -iname "*.aac" -o -iname "*.flac" \
-o -iname "*.ogg" -o -iname "*.opus" -o -iname "*.wma" -o -iname "*.webm" \) -print0)
if [[ ${#files[@]} -eq 0 ]]; then
echo "No audio files found in: $dir"
return 0
fi
local running=0
for f in "${files[@]}"; do
if [[ "$JOBS" -le 1 || $supports_wait_n == false ]]; then
process_one "$f"
else
process_one "$f" &
((running++))
if (( running >= JOBS )); then
wait -n || true
((running--))
fi
fi
done
# Wait for any remaining background jobs
if (( JOBS > 1 )) && $supports_wait_n; then
wait || true
fi
}
main() {
# Sanity checks and notices
if [[ -n "$RN_MODEL" && $use_arnndn == false && $NO_ML == false ]]; then
echo "Note: arnndn filter not available in your ffmpeg or model missing — using afftdn." >&2
fi
if [[ -f "$INPUT_PATH" ]]; then
process_one "$INPUT_PATH"
elif [[ -d "$INPUT_PATH" ]]; then
run_dir "$INPUT_PATH"
else
echo "Error: Input path not found: $INPUT_PATH" >&2
exit 1
fi
}
main "$@"

190
get_rnnoise_model.sh Executable file
View File

@ -0,0 +1,190 @@
#!/usr/bin/env bash
set -euo pipefail
# get_rnnoise_model.sh — fetch an RNNoise model into a local models dir
#
# Prefers known-good rnnoise-nu models. You can override with:
# RN_URL, RN_TARGET_DIR, RN_TARGET_NAME
#
# Usage:
# Bash/get_rnnoise_model.sh # interactive download
# RN_TARGET_DIR=./models Bash/get_rnnoise_model.sh --yes
ask_yes_no() {
read -r -p "$1 [y/N]: " ans || true
case "${ans:-}" in
y|Y|yes|YES) return 0;;
*) return 1;;
esac
}
has_cmd() { command -v "$1" >/dev/null 2>&1; }
YES=false
while [[ $# -gt 0 ]]; do
case "$1" in
-y|--yes) YES=true; shift;;
*) echo "Unknown option: $1" >&2; exit 2;;
esac
done
RN_TARGET_DIR=${RN_TARGET_DIR:-"$(dirname "$0")/models"}
RN_TARGET_NAME=${RN_TARGET_NAME:-"rnnoise_model.rnnn"}
mkdir -p "$RN_TARGET_DIR"
dest="$RN_TARGET_DIR/$RN_TARGET_NAME"
if [[ -f "$dest" ]]; then
echo "Model already exists at: $dest"
exit 0
fi
if ! $YES; then
if ! ask_yes_no "Download RNNoise model to $dest?"; then
echo "Aborted."
exit 1
fi
fi
if ! has_cmd curl && ! has_cmd wget; then
echo "Error: Need curl or wget to download RNNoise model." >&2
exit 3
fi
# Priority 1: explicit URL
if [[ -n "${RN_URL:-}" ]]; then
echo "Downloading RNNoise model from RN_URL: $RN_URL" >&2
tmp=$(mktemp)
if has_cmd curl; then
curl -fsSL "$RN_URL" -o "$tmp"
else
wget -qO "$tmp" "$RN_URL"
fi
if [[ -s "$tmp" ]]; then
mv "$tmp" "$dest"
echo "Saved RNNoise model to: $dest"
exit 0
fi
rm -f "$tmp" || true
echo "Warning: RN_URL download failed; continuing to fallback sources." >&2
fi
# Priority 2: rnnoise-nu known models (GregorR)
NU_URLS=(
"https://raw.githubusercontent.com/GregorR/rnnoise-nu/master/src/models/sh.rnnn"
"https://raw.githubusercontent.com/GregorR/rnnoise-nu/master/src/models/lq.rnnn"
"https://raw.githubusercontent.com/GregorR/rnnoise-nu/master/src/models/mp.rnnn"
"https://raw.githubusercontent.com/GregorR/rnnoise-nu/master/src/models/bd.rnnn"
"https://raw.githubusercontent.com/GregorR/rnnoise-nu/master/src/models/cb.rnnn"
)
for u in "${NU_URLS[@]}"; do
echo "Attempting to download RNNoise model from: $u" >&2
tmp=$(mktemp)
if has_cmd curl; then
if curl -fsSL "$u" -o "$tmp"; then
if [[ -s "$tmp" ]]; then
mv "$tmp" "$dest"
echo "Saved RNNoise model to: $dest" >&2
exit 0
fi
fi
else
if wget -qO "$tmp" "$u"; then
if [[ -s "$tmp" ]]; then
mv "$tmp" "$dest"
echo "Saved RNNoise model to: $dest" >&2
exit 0
fi
fi
fi
rm -f "$tmp" || true
done
# Priority 2b: arnndn-models fallback (richardpl)
RNNDN_URLS=(
"https://raw.githubusercontent.com/richardpl/arnndn-models/master/sh.rnnn"
)
for u in "${RNNDN_URLS[@]}"; do
echo "Attempting to download RNNoise model from: $u" >&2
tmp=$(mktemp)
if has_cmd curl; then
if curl -fsSL "$u" -o "$tmp"; then
if [[ -s "$tmp" ]]; then
mv "$tmp" "$dest"
echo "Saved RNNoise model to: $dest" >&2
exit 0
fi
fi
else
if wget -qO "$tmp" "$u"; then
if [[ -s "$tmp" ]]; then
mv "$tmp" "$dest"
echo "Saved RNNoise model to: $dest" >&2
exit 0
fi
fi
fi
rm -f "$tmp" || true
done
# Priority 3: repo archives (rnnoise-nu and arnndn-models)
ARCHIVES=(
"https://github.com/GregorR/rnnoise-nu/archive/refs/heads/master.zip"
"https://github.com/richardpl/arnndn-models/archive/refs/heads/master.zip"
)
for aurl in "${ARCHIVES[@]}"; do
echo "Attempting to download archive: $aurl" >&2
tmpdir=$(mktemp -d)
archive="$tmpdir/models.zip"
set +e
if has_cmd curl; then
curl -fL "$aurl" -o "$archive"
else
wget -O "$archive" "$aurl"
fi
status=$?
set -e
if [[ $status -ne 0 ]]; then
rm -rf "$tmpdir" || true
continue
fi
if has_cmd bsdtar; then
bsdtar -xf "$archive" -C "$tmpdir"
elif has_cmd unzip; then
unzip -q "$archive" -d "$tmpdir"
else
echo "Warning: Need bsdtar or unzip to extract archive; skipping archive method." >&2
rm -rf "$tmpdir" || true
continue
fi
mapfile -t nnfiles < <(bash -lc 'shopt -s globstar nullglob; for f in '"$tmpdir"'/**/*.rnnn '"$tmpdir"'/**/*.nn; do [[ -f "$f" ]] && echo "$f"; done')
if [[ ${#nnfiles[@]} -gt 0 ]]; then
cp -f "${nnfiles[0]}" "$dest"
echo "Saved RNNoise model to: $dest (from archive)" >&2
rm -rf "$tmpdir" || true
exit 0
fi
rm -rf "$tmpdir" || true
done
# Priority 4: Arch-based AUR packages and search only .nn/.rnnn
if has_cmd yay; then
echo "Attempting to install AUR packages that may include RNNoise models..." >&2
set +e
yay -S --noconfirm denoiseit-git 2>/dev/null
yay -S --noconfirm speech-denoiser-git 2>/dev/null
set -e
mapfile -t found < <(bash -lc 'shopt -s globstar nullglob; for f in /usr/share/**/*.nn /usr/share/**/*.rnnn /usr/local/share/**/*.nn /usr/local/share/**/*.rnnn; do [[ -f "$f" ]] && echo "$f"; done' 2>/dev/null || true)
if [[ ${#found[@]} -gt 0 ]]; then
echo "Found candidate models:" >&2
printf ' %s\n' "${found[@]}" >&2
cp -f "${found[0]}" "$dest"
echo "Copied model to: $dest" >&2
exit 0
fi
fi
echo "Error: Could not obtain an RNNoise model automatically." >&2
echo "Hint: Set RN_URL to a reachable model URL, or place a model file at: $dest" >&2
exit 5

130
install_ffmpeg_with_arnndn.sh Executable file
View File

@ -0,0 +1,130 @@
#!/usr/bin/env bash
set -euo pipefail
# install_ffmpeg_with_arnndn.sh — helper to install/upgrade FFmpeg with arnndn and full audio filters
#
# Tries distro packages first; if not suitable, offers to build from source.
# This script prints commands and asks for confirmation before building.
print_info() {
echo "[info] $*"
}
ask_yes_no() {
read -r -p "$1 [y/N]: " ans || true
case "${ans:-}" in
y|Y|yes|YES) return 0;;
*) return 1;;
esac
}
has_cmd() { command -v "$1" >/dev/null 2>&1; }
detect_distro() {
if [[ -f /etc/os-release ]]; then
. /etc/os-release
echo "${ID:-unknown}"
else
echo "unknown"
fi
}
main() {
local distro
distro=$(detect_distro)
print_info "Detected distro: $distro"
if has_cmd ffmpeg && ffmpeg -hide_banner -filters | grep -q " arnndn "; then
print_info "Your ffmpeg already supports arnndn."
else
case "$distro" in
ubuntu|debian)
print_info "On Ubuntu/Debian, the official repo may lack newer filters. Consider a PPA or build from source."
echo "Options:"
echo " - ppa: sudo add-apt-repository ppa:savoury1/ffmpeg6 && sudo apt update && sudo apt install ffmpeg"
echo " - source build (recommended for latest): run this script to build from source"
;;
arch|manjaro|endeavouros)
print_info "On Arch-based distros, ffmpeg is recent. Try: sudo pacman -Syu ffmpeg"
;;
fedora)
print_info "On Fedora, try: sudo dnf install ffmpeg"
;;
*)
print_info "Distro not recognized; will offer source build."
;;
esac
fi
if ask_yes_no "Build FFmpeg from source with rnnoise/arnndn support now?"; then
echo "This will clone FFmpeg and build locally under ./ffmpeg-build. Continue?"
if ! ask_yes_no "Proceed"; then
exit 0
fi
set -x
mkdir -p ffmpeg-build && cd ffmpeg-build
# Prepare repository
if [[ -d FFmpeg ]]; then
if [[ -d FFmpeg/.git ]]; then
if ask_yes_no "An existing FFmpeg source directory was found. Reuse and update it?"; then
set +e
git -C FFmpeg fetch --all --tags --prune
git -C FFmpeg pull --rebase --ff-only || true
set -e
else
if ask_yes_no "Delete existing FFmpeg directory and re-clone?"; then
rm -rf FFmpeg
else
echo "Keeping existing FFmpeg directory as-is."
fi
fi
else
if ask_yes_no "Non-git 'FFmpeg' directory exists. Delete and re-clone?"; then
rm -rf FFmpeg
else
echo "Cannot proceed with a non-git FFmpeg directory present. Aborting."
exit 4
fi
fi
fi
# Dependencies
if [[ "$distro" == "ubuntu" || "$distro" == "debian" ]]; then
sudo apt update
sudo apt install -y git build-essential yasm nasm pkg-config libx264-dev libx265-dev libvpx-dev libopus-dev libfdk-aac-dev libmp3lame-dev libvorbis-dev libass-dev libfreetype6-dev libgnutls28-dev libaom-dev libdav1d-dev libxvidcore-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev libxcb-shape0-dev libdrm-dev libvulkan-dev libva-dev libvdpau-dev librtmp-dev libunistring-dev libgnutls28-dev libchromaprint-dev libbluray-dev librubberband-dev libspeex-dev libsoxr-dev libvmaf-dev libzimg-dev libsvtav1-dev libtheora-dev libwebp-dev libopenal-dev libjack-jackd2-dev libpulse-dev librnnoise-dev
elif [[ "$distro" == "arch" || "$distro" == "manjaro" || "$distro" == "endeavouros" ]]; then
sudo pacman -Syu --needed base-devel yasm nasm pkgconf rnnoise
elif [[ "$distro" == "fedora" ]]; then
sudo dnf install -y git make gcc yasm nasm pkgconf-pkg-config rnnoise-devel libX11-devel libXext-devel libXfixes-devel libXv-devel libXrandr-devel libXi-devel libXtst-devel libXinerama-devel freetype-devel fontconfig-devel libass-devel libvpx-devel libaom-devel libdav1d-devel zimg-devel rubberband-devel soxr-devel libvorbis-devel opus-devel lame-devel
else
echo "Note: please ensure rnnoise development headers are installed (pkg-config rnnoise)." >&2
fi
if [[ ! -d FFmpeg/.git ]]; then
git clone https://github.com/FFmpeg/FFmpeg.git --depth=1
fi
cd FFmpeg
RN_FLAG=""
# Some FFmpeg versions auto-detect rnnoise without a flag; include the flag only if supported
if ./configure --help | grep -q "librnnoise"; then
RN_FLAG="--enable-librnnoise"
else
echo "[info] configure has no --enable-librnnoise; relying on auto-detection via pkg-config (rnnoise)." >&2
fi
./configure \
--enable-gpl --enable-nonfree \
--enable-libx264 --enable-libx265 --enable-libvpx --enable-libopus --enable-libmp3lame \
--enable-libvorbis --enable-libass --enable-fontconfig --enable-libfreetype \
--enable-librubberband --enable-libsoxr --enable-libzimg --enable-libvmaf \
--enable-libdav1d --enable-libaom --enable-libsvtav1 \
${RN_FLAG} \
--enable-ffplay --enable-ffprobe
make -j"$(nproc)"
echo "Build complete. You can run ./ffmpeg-build/FFmpeg/ffmpeg from this folder or 'sudo make install' to install system-wide."
set +x
else
echo "Skipped building from source."
fi
}
main "$@"