diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-05-19 18:38:39 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-05-19 18:38:39 +0200 |
| commit | e8cf42f2f17151378574ee5b50f29904a1067bee (patch) | |
| tree | 86b4db80971ecf9818ad769bd8b7fa337d218731 /record-output | |
| parent | c3bfa44d1b6da88c184fc3312944cf47d1dfd7f2 (diff) | |
| download | bin-main.tar.gz bin-main.zip | |
Diffstat (limited to 'record-output')
| -rwxr-xr-x | record-output | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/record-output b/record-output new file mode 100755 index 0000000..ab48bdb --- /dev/null +++ b/record-output @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +set -euo pipefail + +OUTPUT="voice" +FORMAT="mp3" +BITRATE="128k" +SOURCE="" +GAIN="6dB" +NORMALIZE=1 +PROBE=0 + +usage() { + cat <<EOF +Usage: $0 -o name [-f mp3|wav] [-b bitrate] [-s source] [-g gain] [-n] [-p] + +Records the currently active audio output (whatever is playing through the +default sink). Auto-detects the correct monitor source. + +Options: + -o NAME Output basename (without extension) + -f FORMAT mp3 (default) or wav + -b BITRATE MP3 bitrate (default 128k) + -s SOURCE Override monitor source (default: auto-detect from default sink) + -g GAIN Pre-normalization gain (default 8dB; set 0dB to disable) + -n No normalization (raw capture) + -p Probe mode: 5s test recording, print levels, exit + -h Help + +Notes: + Pre-gain default (8dB) is tuned for an attenuated chain (e.g. Bluetooth sink + at ~0.35 + app-level stream attenuation). Run -p after any sink or headphone + change; chain attenuation is device-specific. + +Examples: + $0 -o lecture + $0 -o music -f wav -g 6dB + $0 -p # check levels before committing to a long recording +EOF + exit 0 +} + +while getopts "o:f:b:s:g:nph" opt; do + case "$opt" in + o) OUTPUT="$OPTARG" ;; + f) FORMAT="$OPTARG" ;; + b) BITRATE="$OPTARG" ;; + s) SOURCE="$OPTARG" ;; + g) GAIN="$OPTARG" ;; + n) NORMALIZE=0 ;; + p) PROBE=1 ;; + h) usage ;; + *) usage ;; + esac +done + +# Auto-detect monitor of the default sink (what the user is actually hearing) +if [ -z "$SOURCE" ]; then + DEFAULT_SINK=$(pactl get-default-sink) + SOURCE="${DEFAULT_SINK}.monitor" +fi + +# Verify the source exists before invoking ffmpeg +if ! pactl list sources short | awk '{print $2}' | grep -Fxq "$SOURCE"; then + echo "Error: monitor source not found: $SOURCE" >&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" |
