Radio playout control
Palazzo
A self-hosted radio controller combining a NestJS API with Liquidsoap and Icecast.
I designed and built this project independently, using my own resources.
01 / Context
The problem behind the project
An automated radio program needs to coordinate songs, recorded introductions, jingles, and live audio while keeping the stream running.
Palazzo is the execution engine underneath the radio desk. A station’s listener stream needs to remain available while program automation starts, stops, changes songs, or reconnects to its controller. Treating every automation Stop as an Icecast shutdown would turn a routine operator action into a listener interruption.
Another difficult boundary is a recorded introduction that belongs to one particular song. Network retries, queue timing, or a failing asset must not cause that introduction to play over the previous track or be scheduled twice.
02 / Implementation
Design and implementation
I build the playout API, program-scoped automation, and playback lifecycle around the audio transport.
I built a single-container service combining a NestJS control API, a supervised Liquidsoap process, and Icecast. The private API owns readiness, song/instant requests, applied mixer state, durable idempotency, and replay-safe playback events. The public listener mount is kept separate from those machine controls.
Before replacing a song, the service probes real audio packets and validates the media. It reserves the command durably, preloads a recorded intro, and arms that intro for the exact parent playback ID. Liquidsoap releases it only when the matching song starts. The intro can duck and fade the song while manual jingles remain on a separate bus.
I separated transport health from automation state and playback truth. On restart the engine requires reconciliation; a running container cannot stand in for confirmation that an earlier Start succeeded. Stop flushes song, intro, and instant queues, waits for observed idle, and leaves the continuous listener transport connected.
03 / Architecture
Architecture and workflow
Preflight upcoming media
Validate format, codec, duration, channels, and the presence of decodable audio before changing the current playout.
Reserve one operation
Persist the idempotency key and semantic request. A retry reuses the original playback IDs; a different request cannot reuse the same key.
Coordinate song and intro
Arm the intro against its parent song, then replace the song. An invalid song rejects the operation; an unavailable intro follows the explicit song-only policy.
Report engine truth
Playback IDs travel through Liquidsoap telemetry, authoritative snapshots, and replay-safe SSE events.
Preserve transport
Automation lifecycle changes queues and program state while Liquidsoap and the Icecast mount remain running.
04 / Approach
Engineering decisions
Keep transport and automation distinct
Program automation can start or stop without unnecessarily restarting the Icecast stream.
Treat a song and its introduction as one operation
Program-scoped playout coordinates song and intro behavior with durable idempotency and explicit lifecycle reporting.
05 / Evidence
Evidence and outcomes
The program-scoped playout contract includes duplicate commands, restarts, preflight failures, intro degradation, and control/telemetry outages. The durable journal preserves command identity across a process or container restart.
The output-monitor integration forwards encoded Icecast bytes incrementally through the private backend path. Closing an operator’s monitor connection closes that relay connection without stopping broadcast playback.
Levels, positions, heartbeats, dependency readiness, and bounded failure outcomes provide separate evidence about audio production and control health. Those observations feed Alcántara’s now-playing and output-confidence surfaces.
Source and technical notes
06 / Current scope
Where it stands
Radio-control development alongside the wider Alcántara production system.
Queue exhaustion, silence, or loss of the controller does not invent an explicit Stop. Requested automation and actual dependency state can differ, and degraded state remains visible.
Palazzo executes playout; Alcántara owns scheduling and operator intent. Source validation can reject corrupt or unreachable media, but it cannot establish editorial suitability or perceptual quality.