#!/usr/bin/env bash set -euo pipefail OUTPUT="voice" FORMAT="mp3" BITRATE="128k" SOURCE="" GAIN="6dB" NORMALIZE=1 PROBE=0 usage() { cat <&2 echo "Available sources:" >&2 pactl list sources short | awk '{print " " $2}' >&2 exit 1 fi # Build audio filter chain FILTER="" if [ "$NORMALIZE" -eq 1 ]; then FILTER="volume=${GAIN},loudnorm=I=-16:TP=-1.5:LRA=11" elif [ "$GAIN" != "0dB" ]; then FILTER="volume=${GAIN}" fi FORMAT=$(echo "$FORMAT" | tr '[:upper:]' '[:lower:]') # Probe mode: short capture, report levels, exit if [ "$PROBE" -eq 1 ]; then echo "Probing $SOURCE for 5 seconds..." PROBE_OUT=$(ffmpeg -hide_banner -nostats -loglevel info \ -f pulse -i "$SOURCE" -t 5 -af "volumedetect" -f null - 2>&1) || true LEVELS=$(echo "$PROBE_OUT" | grep -E "mean_volume|max_volume" || true) if [ -z "$LEVELS" ]; then echo "No level data — source emitted silence or ffmpeg failed." echo echo "ffmpeg output (last 20 lines):" echo "$PROBE_OUT" | tail -20 else echo "$LEVELS" echo echo "Current gain: -g ${GAIN}" echo echo "Reference:" echo " mean_volume near -16 dB good for voice" echo " mean_volume below -25 dB increase -g or raise sink volume" echo " max_volume above -1 dB reduce -g (risk of clipping)" fi exit 0 fi # Assemble ffmpeg invocation FILE="${OUTPUT}.${FORMAT}" ARGS=(-f pulse -i "$SOURCE" -ac 2) [ -n "$FILTER" ] && ARGS+=(-af "$FILTER") case "$FORMAT" in mp3) ARGS+=(-c:a libmp3lame -b:a "$BITRATE") ;; wav) : ;; *) echo "Error: unsupported format '$FORMAT' (use mp3 or wav)" >&2 exit 1 ;; esac echo "Recording: $SOURCE" echo "Filter: ${FILTER:-(none)}" echo "Output: $FILE" echo "Press Ctrl+C to stop." echo ffmpeg "${ARGS[@]}" "$FILE"