Skip to content

audioio: report a failed sound card instead of dying quietly - #173

Open
rafael2k wants to merge 1 commit into
mercuryv2from
audio-health
Open

audioio: report a failed sound card instead of dying quietly#173
rafael2k wants to merge 1 commit into
mercuryv2from
audio-health

Conversation

@rafael2k

Copy link
Copy Markdown
Contributor

When a capture or playback device fails to open, mercury gave no usable signal
about it — the operator sees a station that is up but deaf or mute, with
nothing in the UI to say which.

Adds an audio_health_t state per direction, set at the point of failure, and
plain-typed accessors (audioio_capture_health(), audioio_playback_health(),
audioio_health_reason(), audioio_health_ok(char*, size_t)) so callers need
no audioio header. Surfaced through ui_status.

Deliberately does not exit: picking the wrong sound card should let the
operator pick another one, not kill the process — per @rafael2k.

History, for the reviewer

This is a9effec, written during the #162 audio work. I pushed it into #163
by mistake, where it had nothing to do with FFT acquisition; it was stripped
back out so #163 stayed single-purpose, which left this stranded in reflog.
Recovered and landed on its own here.

It cherry-picks onto trunk cleanly now — the tests/Makefile conflict it hit
earlier was against a trunk that predated #163's changes to the same file.

Testing

Full build clean, unit suite green (All Tests Passed) from a clean tests tree
on current trunk cb4ab1b.

Relevant to #162: the reporter was selecting sound cards while diagnosing, which
is exactly the case where a silent failure costs the most time.

22050 and 44100 are refused, and should be: neither is an integer multiple of
the 8 kHz modem rate, and approximating with the nearest integer ratio
transmits off-frequency at the correct level and spectrally pure -- a 1 kHz
tone measured 166.8 Hz on a device pinned to 8 kHz. Nothing about that looks
wrong until you count cycles. The rejection is right and stays.

What was wrong is what happens next. The check runs inside the capture and
playback threads, after pthread_create has already returned 0, so
audioio_init_internal reports success and the engine starts normally. The
thread logs an error and exits. Mercury then runs with TCP ports open, the UI
attached and the status payload describing a perfectly healthy station -- while
hearing nothing and transmitting nothing. The one place an operator would look
says everything is fine.

Not fixed by exiting. A wrong sound card is exactly the case where the operator
needs mercury alive: audioio_restart() and the UI device list exist so they can
pick another one, and a process that quits leaves them nothing to pick with.

So the threads now record health (audio_health_t: STOPPED/RUNNING/FAILED plus a
reason) and the status carries audio_ok and audio_error. A failed card becomes
visible in the UI, mercury stays up, and the operator can choose another
device. Wired at the two failure classes that actually happen: an unsupported
rate, and a device that will not open (wrong card, or busy).

The rate message now names the fix rather than just the problem -- "try
plughw:X,Y instead of hw:X,Y so ALSA converts". That is the operator's actual
remedy, it is what mercury.ini.example already recommends, and it is why almost
nobody hits this: ALSA's plug layer converts 44100/22050 transparently and
mercury only ever sees 48000.

Also: test_ui_status had a build rule but was NOT in TEST_BINS, so the test
whose entire purpose is to catch drift between the status struct and the JSON
on the wire has never run in CI. It caught this change only because I ran it by
hand. Added to the suite -- it was the only orphan; the other 18 rules are all
listed.

Not implemented: rational resampling for the 44.1 kHz family. It is a real
option (441:80 polyphase, ~30 MACs per output sample) but plughw already covers
it, and widening what the audio path accepts deserves its own change rather
than riding along with a bug fix.

Gate: unit suite green (19 binaries), integration go test -count=1 green
(257 s).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant