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
+6 -1
View File
@@ -1,7 +1,7 @@
{
"id": "propresenter-timers",
"name": "Timers",
"version": "1.0.0",
"version": "1.1.0",
"description": "Live ProPresenter timers and LTC timecode",
"client": "client.js",
"server": "server.js",
@@ -23,6 +23,11 @@
"type": "password",
"label": "Stage display password (for LTC — ProPresenter Settings → Network)",
"default": ""
},
"ingestToken": {
"type": "password",
"label": "LTC reader token (optional — must match tools/ltc-reader.js --token)",
"default": ""
}
}
}