morse: remove select()-based pipe polling for capture/output

This commit is contained in:
Smittix
2026-02-26 17:29:41 +00:00
parent 9c8e819046
commit 32e3eb4eb2
+56 -65
View File
@@ -8,7 +8,6 @@ import os
import pty import pty
import queue import queue
import re import re
import select
import struct import struct
import subprocess import subprocess
import tempfile import tempfile
@@ -177,43 +176,6 @@ def _compress_amplitudes(samples: tuple[int, ...], bins: int = 96) -> list[int]:
return out return out
def _read_pcm_chunk(
stream: Any,
chunk_bytes: int,
stop_event: threading.Event,
timeout_s: float = 0.2,
) -> bytes | None:
if stream is None:
return b''
try:
fileno = stream.fileno()
except Exception:
if stop_event.is_set():
return b''
with contextlib.suppress(Exception):
return stream.read(chunk_bytes)
return b''
while not stop_event.is_set():
try:
ready, _, _ = select.select([fileno], [], [], timeout_s)
except Exception:
return b''
if not ready:
return None
try:
return os.read(fileno, chunk_bytes)
except BlockingIOError:
continue
except OSError:
return b''
return b''
def _morse_audio_relay_thread( def _morse_audio_relay_thread(
rtl_stdout: Any, rtl_stdout: Any,
multimon_stdin: Any, multimon_stdin: Any,
@@ -241,6 +203,16 @@ def _morse_audio_relay_thread(
threshold = manual_threshold if threshold_mode == 'manual' else 0.0 threshold = manual_threshold if threshold_mode == 'manual' else 0.0
pcm_announced = False pcm_announced = False
fd: int | None = None
non_blocking = False
if rtl_stdout is not None:
with contextlib.suppress(Exception):
fd = rtl_stdout.fileno()
if fd is not None:
with contextlib.suppress(Exception):
os.set_blocking(fd, False)
non_blocking = True
try: try:
while not stop_event.is_set(): while not stop_event.is_set():
if control_queue is not None: if control_queue is not None:
@@ -264,7 +236,20 @@ def _morse_audio_relay_thread(
if stop_event.is_set(): if stop_event.is_set():
break break
payload = _read_pcm_chunk(rtl_stdout, chunk_bytes, stop_event) payload: bytes | None = None
if non_blocking and fd is not None:
try:
payload = os.read(fd, chunk_bytes)
except BlockingIOError:
payload = None
except OSError:
payload = b''
else:
try:
payload = rtl_stdout.read(chunk_bytes)
except Exception:
payload = b''
now = time.monotonic() now = time.monotonic()
if payload is None: if payload is None:
@@ -283,6 +268,7 @@ def _morse_audio_relay_thread(
'tone_freq': tone_freq, 'tone_freq': tone_freq,
'wpm': wpm, 'wpm': wpm,
}) })
time.sleep(0.02)
continue continue
if not payload: if not payload:
@@ -341,7 +327,8 @@ def _morse_audio_relay_thread(
'wpm': wpm, 'wpm': wpm,
}) })
except Exception as exc: except Exception as exc:
logger.debug('Morse audio relay error: %s', exc) logger.warning('Morse audio relay error: %s', exc)
_queue_morse_event({'type': 'error', 'text': f'morse relay error: {exc}'})
finally: finally:
_close_pipe(multimon_stdin) _close_pipe(multimon_stdin)
@@ -352,38 +339,42 @@ def _morse_multimon_output_thread(
stop_event: threading.Event, stop_event: threading.Event,
) -> None: ) -> None:
buffer = '' buffer = ''
with contextlib.suppress(Exception):
os.set_blocking(master_fd, False)
try: try:
while not stop_event.is_set(): while not stop_event.is_set():
raw: bytes | None = None
try: try:
ready, _, _ = select.select([master_fd], [], [], 0.2) raw = os.read(master_fd, 2048)
except Exception: except BlockingIOError:
raw = None
except OSError:
break break
if ready: if raw is None:
try: if process.poll() is not None:
raw = os.read(master_fd, 2048)
except OSError:
break break
if not raw: time.sleep(0.02)
if process.poll() is not None: continue
break
if not raw:
if process.poll() is not None:
break
time.sleep(0.02)
continue
buffer += raw.decode('utf-8', errors='replace')
while '\n' in buffer:
line, buffer = buffer.split('\n', 1)
line = line.strip()
if not line:
continue continue
text = _parse_multimon_morse_text(line)
buffer += raw.decode('utf-8', errors='replace') if text is None:
while '\n' in buffer: _queue_morse_event({'type': 'info', 'text': f'[multimon] {line}'})
line, buffer = buffer.split('\n', 1) continue
line = line.strip() if text:
if not line: _emit_decoded_text(text)
continue
text = _parse_multimon_morse_text(line)
if text is None:
_queue_morse_event({'type': 'info', 'text': f'[multimon] {line}'})
continue
if text:
_emit_decoded_text(text)
if process.poll() is not None:
break
tail = buffer.strip() tail = buffer.strip()
if tail: if tail: