← Whispering

fix: Clean shutdown on Ctrl+C and add monitor device debugging

99ea29e89985ba1518fa40a550ace51e4fb68c6c · Claude · Jan 25, 2026 23:44

- Handle KeyboardInterrupt gracefully to avoid ugly traceback on Ctrl+C
- Add debug_list_all_devices() to show all sounddevice devices
- Enable debug mode for monitor detection on startup to diagnose issues

+33 −4 · 2 files changed

  • src/core_parts/audio_devices.py +25 −2
  • src/whispering_ui/main.py +8 −2
diff --git a/src/core_parts/audio_devices.py b/src/core_parts/audio_devices.py
index 6823e9d..014d4ec 100644
--- a/src/core_parts/audio_devices.py
+++ b/src/core_parts/audio_devices.py
@@ -99,14 +99,32 @@ def _extract_friendly_name(device_name):
     return name
 
 
-def get_monitor_names():
+def debug_list_all_devices():
+    """Print all audio devices for debugging purposes."""
+    devices_list = sd.query_devices()
+    hostapis = sd.query_hostapis()
+
+    print("\n=== All Audio Devices ===")
+    for i, d in enumerate(devices_list):
+        api_name = hostapis[d['hostapi']]['name']
+        in_ch = d['max_input_channels']
+        out_ch = d['max_output_channels']
+        print(f"  [{i}] {d['name']}")
+        print(f"      API: {api_name}, In: {in_ch}, Out: {out_ch}")
+    print("========================\n")
+
+
+def get_monitor_names(debug=False):
     """Get list of speaker/output monitor device names (for capturing system audio output).
 
     Monitor devices capture what's being played through speakers/headphones.
-    Returns tuples of (device_index, friendly_display_name, original_name).
+    Returns tuples of (device_index, friendly_display_name).
     """
     devices_list = sd.query_devices()
 
+    if debug:
+        debug_list_all_devices()
+
     monitors = []
 
     for i, d in enumerate(devices_list):
@@ -123,6 +141,11 @@ def get_monitor_names():
             if 'monitor' in name_lower:
                 friendly_name = _extract_friendly_name(name)
                 monitors.append((i, friendly_name))
+                if debug:
+                    print(f"  Found monitor: [{i}] {name} -> '{friendly_name}'")
+
+    if debug and not monitors:
+        print("  No monitor devices found!")
 
     return monitors
 
diff --git a/src/whispering_ui/main.py b/src/whispering_ui/main.py
index 0d73c38..568c070 100644
--- a/src/whispering_ui/main.py
+++ b/src/whispering_ui/main.py
@@ -64,7 +64,8 @@ def main():
         state.mic_list = []
 
     try:
-        state.monitor_list = core.get_monitor_names()
+        # Use debug=True to see all devices on startup
+        state.monitor_list = core.get_monitor_names(debug=True)
     except Exception as e:
         print(f"Error getting monitor list: {e}")
         state.monitor_list = []
@@ -444,4 +445,9 @@ def main():
 
 
 if __name__ == "__main__":
-    main()
+    try:
+        main()
+    except KeyboardInterrupt:
+        print("\n👋 Shutting down...")
+    except SystemExit:
+        pass