Skip to content

Playout

heron-engine is the real-time playout process. It needs no sound card: it renders audio against the system clock, 10 ms at a time, and keeps time exactly over days of streaming.

Tracks alternate between Deck A and Deck B. The engine holds the queue; with autofill on, the API keeps it filled with rotation’s picks. From On air and Queue you can skip, pause, resume, reorder, play a track next, or remove it.

  • Skip fades the current track out (1 s by default) and starts the next at once.
  • Pause fades out in 10 ms and holds the decks; the stream keeps going with silence.

For each transition, the engine plans where the next track starts and how the two are faded:

  1. Where: at the analyzed crossfade point (auto), or a fixed time before the cue-out (fixed).
  2. Back to back instead when either track is 20 s or shorter (jingles, IDs), or crossfading is off for the track.
  3. Fade-out of the outgoing track, 2.5 s by default.
  4. Fade-in of the incoming track, off by default.

Fade curves: linear, S-curve, logarithmic or exponential. Silences at both ends of every track are cut at its cue points (Gap Killer).

Adjust all of this in Crossfade & loudness (admins), with a live preview of the transition between the track on air and the next. A track’s page can override the crossfade, the cue points and Gap Killer for that track alone.

Each track gets a constant gain that brings its integrated loudness to the target (−16 LUFS by default). Boosts stop at +12 dB and never push the true peak above −1 dBTP. There is no limiter yet: AGC and multiband processing are on the roadmap.

The FX player plays a jingle, liner or sweeper on top of the decks, from the On air page. It does not change what is on air.

Every failure ends with audio continuing: the next track first, then the fallback folder, and generated silence only as the very last resort, with an alert.

Failure What happens
A queued file is missing or corrupt Upcoming tracks are read into memory ahead of time, so a file deleted later still plays; a file that cannot be read is skipped
The library is slow or unreachable The next tracks are already in memory
The queue runs dry (API down, autofill off, restrictions forbid everything) A track from the fallback folder plays
A track stays silent The silence watchdog skips it after 20 s
A stream server restarts The output reconnects on its own; the others are not affected
The engine restarts It resumes the track on air where it was, then the saved queue

HERON_FALLBACK_DIR holds a few tracks that suit any hour: station IDs and evergreen music. Files are scanned every minute and analyzed once, so they play with proper crossfades and loudness. Each plays once, in random order, before any plays again.

heron-engine ctl inspects or drives the engine without the API:

Terminal window
docker compose exec heron-engine heron-engine ctl state
docker compose exec heron-engine heron-engine ctl watch
docker compose exec heron-engine heron-engine ctl skip