Timers: LTC from the audio signal itself via an external reader

ProPresenter 21.4 exposes no timecode through any API, so read the LTC
where it actually lives — the audio feed:

- tools/ltc-reader.js: zero-dependency SMPTE LTC decoder (biphase-mark,
  adaptive bit clock, envelope-scaled comparator) fed s16le PCM on stdin
  from ffmpeg/sox; POSTs running/stopped/nosignal + timecode + frame rate
  (incl. 29.97 drop-frame) to the module. --selftest runs an encoder→
  decoder suite (24/25/30 fps, DF minute counting, low level + noise + DC,
  dropout recovery); --demo synthesizes live LTC through the real decoder
  and ingest path with no audio hardware.
- Module: POST /ltc ingest route (optional bearer token, validated body,
  6 s staleness with automatic fallback to the API probe / stage source),
  richer LTC state (status, fps, df, reader source), health messages.
- Client: LTC card shows Running · <rate> / Stopped (held frame) /
  No signal; works LTC-only with no ProPresenter host configured; the
  reader-fed card no longer grays out when only ProPresenter is down.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Waters Church
2026-09-01 14:52:20 -04:00
co-authored by Claude Fable 5
parent d5cb6b6cd9
commit cb77030eec
6 changed files with 744 additions and 34 deletions
+14 -4
View File
@@ -91,10 +91,20 @@ are written by the app itself.
elapsed) and LTC timecode as auto-sized cards. The + picker lists every
discovered timer (and the LTC card) as its own tile, or add "All timers"
and pick a selection via the tile's "Timers ▾" menu; overruns are
unmistakably red. LTC is read from a stage-display layout field labeled
"LTC" over ProPresenter's stage websocket (its HTTP API has no timecode
route). Read-only — no start/stop controls. Admin config: ProPresenter
endpoint (host : port), API password, stage display password.
unmistakably red. Read-only — no start/stop controls. Admin config:
ProPresenter endpoint (host : port), API password, stage display
password, LTC reader token.
LTC sources, best first (ProPresenter's HTTP API has no timecode route):
1. **The LTC audio feed itself** — run `tools/ltc-reader.js` on any
machine that can hear the LTC signal; it decodes the SMPTE frames from
a PCM pipe (ffmpeg/sox one-liners in its header) and POSTs
running/stopped/no-signal, the timecode, and the frame rate (incl.
29.97 drop-frame) to the module's `/ltc` ingest route. `--selftest`
checks the decoder; `--demo` feeds synthetic LTC end-to-end with no
audio hardware.
2. A stage-display layout field labeled "LTC", read over ProPresenter's
stage websocket — value and freshness only, no frame rate.
- **Clock** — a big booth clock; also the smallest possible module and the
reference for module authors.