aboutsummaryrefslogtreecommitdiff
path: root/record-output
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-05-19 18:38:39 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-05-19 18:38:39 +0200
commite8cf42f2f17151378574ee5b50f29904a1067bee (patch)
tree86b4db80971ecf9818ad769bd8b7fa337d218731 /record-output
parentc3bfa44d1b6da88c184fc3312944cf47d1dfd7f2 (diff)
downloadbin-e8cf42f2f17151378574ee5b50f29904a1067bee.tar.gz
bin-e8cf42f2f17151378574ee5b50f29904a1067bee.zip
added readmeHEADmain
Diffstat (limited to 'record-output')
-rwxr-xr-xrecord-output124
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"