summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-11-03 13:51:02 -0600
committerCraig Jennings <c@cjennings.net>2025-11-03 13:51:02 -0600
commitcf90eada0d16133f860b31f618ce0a5fb3468c75 (patch)
treef9fe44569a8c78ef654749bf5eec51e11b3706ed /modules
parent6534147e502f3f1d504b8d2d9757aefa04bfbb07 (diff)
feat: Add device selection and diagnostics to recording module
Add interactive device selection to fix recording with multiple audio devices. New features: - cj/recording-list-devices (C-; r d) - Show all available audio sources - cj/recording-select-devices (C-; r s) - Interactively select mic/monitor - cj/recording-parse-sources - Parse pactl output into structured data - Enhanced cj/recording-get-devices with graceful fallback to manual selection Improvements: - Works with PulseAudio and PipeWire - Supports USB devices (Jabra SPEAK 510) - Supports Bluetooth devices - Supports built-in laptop audio - Shows device state (RUNNING, SUSPENDED) during selection - Better error messages with actionable suggestions - Device selection persists across recordings Fixes recording breakage when plugging in external audio interfaces. Addresses Method 1 (Make Using Emacs Frictionless) from V2MOM. Closes #[#B] Fix video/audio recording module sub-tasks: - Add diagnostic command - Add device selection UI - Improve error messages - Make device detection more flexible 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'modules')
-rw-r--r--modules/video-audio-recording.el83
1 files changed, 80 insertions, 3 deletions
diff --git a/modules/video-audio-recording.el b/modules/video-audio-recording.el
index 73f782f6..f9311957 100644
--- a/modules/video-audio-recording.el
+++ b/modules/video-audio-recording.el
@@ -69,6 +69,74 @@ Returns device name or nil if not found."
(when (string-match "\\([^\t\n]+\\.monitor\\)" output)
(match-string 1 output))))
+(defun cj/recording-parse-sources ()
+ "Parse pactl sources output into structured list.
+Returns list of (device-name description state) tuples."
+ (let ((output (shell-command-to-string "pactl list sources short 2>/dev/null"))
+ (sources nil))
+ (dolist (line (split-string output "\n" t))
+ (when (string-match "^[0-9]+\t\\([^\t]+\\)\t\\([^\t]+\\)\t\\([^\t]+\\)\t\\([^\t]+\\)" line)
+ (let ((device (match-string 1 line))
+ (driver (match-string 2 line))
+ (state (match-string 4 line)))
+ (push (list device driver state) sources))))
+ (nreverse sources)))
+
+(defun cj/recording-list-devices ()
+ "Show all available audio sources in a readable format.
+Opens a buffer showing devices with their states."
+ (interactive)
+ (let ((sources (cj/recording-parse-sources)))
+ (with-current-buffer (get-buffer-create "*Recording Devices*")
+ (erase-buffer)
+ (insert "Available Audio Sources\n")
+ (insert "========================\n\n")
+ (insert "Current Configuration:\n")
+ (insert (format " Microphone: %s\n" (or cj/recording-mic-device "Not set")))
+ (insert (format " System Audio: %s\n\n" (or cj/recording-system-device "Not set")))
+ (insert "Available Devices:\n\n")
+ (if sources
+ (dolist (source sources)
+ (let ((device (nth 0 source))
+ (driver (nth 1 source))
+ (state (nth 2 source)))
+ (insert (format "%-10s [%s]\n" state driver))
+ (insert (format " %s\n\n" device))))
+ (insert " No audio sources found. Is PulseAudio/PipeWire running?\n"))
+ (goto-char (point-min))
+ (special-mode))
+ (switch-to-buffer-other-window "*Recording Devices*")))
+
+(defun cj/recording-select-device (prompt device-type)
+ "Interactively select an audio device.
+PROMPT is shown to user. DEVICE-TYPE is 'mic or 'monitor for filtering.
+Returns selected device name or nil."
+ (let* ((sources (cj/recording-parse-sources))
+ (filtered (if (eq device-type 'monitor)
+ (seq-filter (lambda (s) (string-match-p "\\.monitor$" (car s))) sources)
+ (seq-filter (lambda (s) (not (string-match-p "\\.monitor$" (car s)))) sources)))
+ (choices (mapcar (lambda (s)
+ (let ((device (nth 0 s))
+ (driver (nth 1 s))
+ (state (nth 2 s)))
+ (cons (format "%-10s %s" state device) device)))
+ filtered)))
+ (if choices
+ (cdr (assoc (completing-read prompt choices nil t) choices))
+ (user-error "No %s devices found" (if (eq device-type 'monitor) "monitor" "input")))))
+
+(defun cj/recording-select-devices ()
+ "Interactively select microphone and system audio devices.
+Sets cj/recording-mic-device and cj/recording-system-device."
+ (interactive)
+ (setq cj/recording-mic-device
+ (cj/recording-select-device "Select microphone device: " 'mic))
+ (setq cj/recording-system-device
+ (cj/recording-select-device "Select system audio monitor: " 'monitor))
+ (message "Devices set - Mic: %s, System: %s"
+ cj/recording-mic-device
+ cj/recording-system-device))
+
(defun cj/recording-get-devices ()
"Get or auto-detect audio devices.
Returns (mic-device . system-device) or nil on error."
@@ -78,9 +146,14 @@ Returns (mic-device . system-device) or nil on error."
(unless cj/recording-system-device
(setq cj/recording-system-device (cj/recording-detect-system-device)))
- ;; Validate devices
+ ;; If auto-detection failed, prompt user to select
+ (unless (and cj/recording-mic-device cj/recording-system-device)
+ (when (y-or-n-p "Could not auto-detect audio devices. Select manually? ")
+ (cj/recording-select-devices)))
+
+ ;; Final validation
(unless (and cj/recording-mic-device cj/recording-system-device)
- (user-error "Could not detect audio devices. Set cj/recording-mic-device and cj/recording-system-device manually"))
+ (user-error "Audio devices not configured. Run M-x cj/recording-select-devices"))
(cons cj/recording-mic-device cj/recording-system-device))
@@ -222,6 +295,8 @@ Otherwise use the default location in `audio-recordings-dir'."
(define-key map (kbd "A") #'cj/audio-recording-stop)
(define-key map (kbd "a") #'cj/audio-recording-start)
(define-key map (kbd "l") #'cj/recording-adjust-volumes)
+ (define-key map (kbd "d") #'cj/recording-list-devices)
+ (define-key map (kbd "s") #'cj/recording-select-devices)
map)
"Keymap for video/audio recording operations.")
@@ -234,7 +309,9 @@ Otherwise use the default location in `audio-recordings-dir'."
"C-; r V" "stop video"
"C-; r a" "start audio"
"C-; r A" "stop audio"
- "C-; r l" "adjust levels"))
+ "C-; r l" "adjust levels"
+ "C-; r d" "list devices"
+ "C-; r s" "select devices"))
(provide 'video-audio-recording)
;;; video-audio-recording.el ends here.