Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions LivescriptV2.01.py
Original file line number Diff line number Diff line change
Expand Up @@ -976,12 +976,53 @@ def quit_app(self):


# --- Background Threads ---
def is_loopback_microphone(microphone):
"""Return True for PulseAudio/PipeWire monitor or other loopback sources."""
mic_id = str(microphone.id).lower()
mic_name = microphone.name.lower()
return "monitor" in mic_id or "loopback" in mic_id or "monitor" in mic_name or "loopback" in mic_name


def get_loopback_microphone(speaker):
"""Find the recording source that monitors the given speaker output."""
speaker_id = str(speaker.id)
candidates = [
f"{speaker_id}.monitor", # PulseAudio/PipeWire monitor source id on Linux.
speaker_id,
speaker.name,
f"Monitor of {speaker.name}",
]

for candidate in candidates:
try:
return sc.get_microphone(id=candidate, include_loopback=True)
except Exception:
pass

loopbacks = [mic for mic in sc.all_microphones(include_loopback=True) if is_loopback_microphone(mic)]
matching_loopbacks = [mic for mic in loopbacks if speaker_id in str(mic.id) or speaker.name in mic.name]

if matching_loopbacks:
return matching_loopbacks[0]
if len(loopbacks) == 1:
return loopbacks[0]

available = "\n".join(f" {mic.name} | id={mic.id}" for mic in sc.all_microphones(include_loopback=True))
raise RuntimeError(
"Could not find a loopback/monitor microphone for the default speaker.\n"
"Available microphones including loopback devices:\n"
f"{available}"
)


def audio_capture_thread(block_size, sample_rate):
"""Captures audio from the default speaker and puts it into a queue."""
try:
default_speaker = sc.default_speaker()
print(f"Using speaker: {default_speaker.name} for audio capture.")
with sc.get_microphone(id=str(default_speaker.id), include_loopback=True).recorder(
loopback_microphone = get_loopback_microphone(default_speaker)
print(f"Using loopback microphone: {loopback_microphone.name} for audio capture.")
with loopback_microphone.recorder(
samplerate=sample_rate, channels=1, blocksize=int(block_size)
) as mic:
print("Audio recorder started.")
Expand Down Expand Up @@ -1107,4 +1148,4 @@ def restart_application():


if __name__ == "__main__":
main()
main()