🔧 Where my dotfiles lives in harmony and peace, most of the time
1#!/usr/bin/env bash
2set -euo pipefail
3
4error() {
5 printf "✗ %s\n" "$*" >&2
6 exit 1
7}
8
9usage() {
10 cat <<'EOF'
11Usage:
12 record-audio [options] [output-file]
13
14Records system audio (default sink monitor) and microphone (default source) using ffmpeg (MP3).
15Stops on Ctrl+C.
16
17Options:
18 --system-only Record only system audio (what you hear)
19 --mic-only Record only microphone
20 -o, --out FILE Output file
21 -h, --help Show help
22
23Default output:
24 recording-YYYYmmdd-HHMMSS.mp3
25EOF
26}
27
28require_cmd() {
29 command -v "$1" >/dev/null 2>&1 || error "Missing dependency: $1"
30}
31
32timestamp() {
33 date +%Y%m%d-%H%M%S
34}
35
36main() {
37 local mode="mix"
38 local out=""
39
40 while [[ $# -gt 0 ]]; do
41 case $1 in
42 --system-only)
43 mode="system"
44 shift
45 ;;
46 --mic-only)
47 mode="mic"
48 shift
49 ;;
50 -o|--out)
51 out="${2:-}"
52 [[ -n $out ]] || error "--out requires a file path"
53 shift 2
54 ;;
55 -h|--help)
56 usage
57 exit 0
58 ;;
59 *)
60 if [[ -z $out ]]; then
61 out="$1"
62 shift
63 else
64 error "Unknown argument: $1"
65 fi
66 ;;
67 esac
68 done
69
70 require_cmd pactl
71 require_cmd ffmpeg
72
73 if [[ -z $out ]]; then
74 out="recording-$(timestamp).mp3"
75 fi
76
77 if [[ -e $out ]]; then
78 error "Refusing to overwrite existing file: $out"
79 fi
80
81 local sink
82 local source
83 sink="$(pactl get-default-sink)"
84 source="$(pactl get-default-source)"
85
86 [[ -n $sink ]] || error "No default sink found"
87 [[ -n $source ]] || error "No default source found"
88
89 local monitor="${sink}.monitor"
90
91 printf "Recording mode: %s\n" "$mode"
92 printf "Default sink: %s\n" "$sink"
93 printf "Default source: %s\n" "$source"
94 printf "Output: %s\n" "$out"
95 printf "Stop: Ctrl+C\n"
96
97 case $mode in
98 system)
99 exec ffmpeg -f pulse -i "$monitor" -c:a libmp3lame -q:a 4 "$out"
100 ;;
101 mic)
102 exec ffmpeg -f pulse -i "$source" -c:a libmp3lame -q:a 4 "$out"
103 ;;
104 mix)
105 exec ffmpeg \
106 -f pulse -i "$monitor" \
107 -f pulse -i "$source" \
108 -filter_complex '[0:a]aformat=sample_rates=48000:channel_layouts=stereo[a0];[1:a]aformat=sample_rates=48000:channel_layouts=stereo[a1];[a0][a1]amix=inputs=2:duration=longest:dropout_transition=0' \
109 -c:a libmp3lame -q:a 4 \
110 "$out"
111 ;;
112 *)
113 error "Unknown mode: $mode"
114 ;;
115 esac
116}
117
118main "$@"