A community radio station does not need a rack of studio automation software. If your programming is mostly pre-recorded, your live inserts are short, and your audience is local, the job reduces to: “play the right file at the right time, and let the presenter speak over the output when they want to.” That’s a Multilarm config.
A small community broadcaster with a low-power FM transmitter and a live internet stream. The daily schedule is a mix of recorded shows, pre-recorded interviews, station IDs, and a handful of live windows where a presenter speaks from home. There is no studio staffed 24/7 — the feed runs itself most of the time.
systemd unit, restarted on crashThe FM transmitter and the streaming encoder both need the same audio, in sync, at the same moment. That is exactly what MirrorDevices is for:
<MAConfig><PlaybackDevice>2</PlaybackDevice></MAConfig>
<MAConfig><MirrorDevices>3</MirrorDevices></MAConfig>
Device 2 goes to the transmitter, device 3 to the encoder. Both are Ctrl+O-verified and labelled on the dongles. A listener on FM and a listener on the internet stream hear the same word at the same moment — which is the whole point of mirroring at the audio-buffer level rather than splitting downstream.
Traditional automation software thinks in playlists. Multilarm thinks in folders. A show becomes a folder; the schedule fires that folder at its slot; the engine picks a track at random and logs what it played. DateAndTimeData carries one time per slot, AlarmIndexData routes each slot to a folder in AlarmPath:
<MAConfig><RecurEveryDay>True</RecurEveryDay></MAConfig>
<MAConfig><TimeDelimiter>|</TimeDelimiter></MAConfig>
<MAConfig><AlarmPath>/srv/radio/Shows/MorningMix|/srv/radio/IDs|/srv/radio/News/Today|/srv/radio/Shows/Afternoon|/srv/radio/Shows/Overnight</AlarmPath></MAConfig>
<MAConfig><AlarmIndexData>5|1|2|3|4</AlarmIndexData></MAConfig>
<MAConfig><DateAndTimeData>0:00|8:00|9:00|9:02|13:00</DateAndTimeData></MAConfig>
<MAConfig><AlarmFileFormat>mp3</AlarmFileFormat></MAConfig>
Slots fire in ascending order: 00:00 (index 5, Overnight), 08:00 (MorningMix), 09:00 (IDs), 09:02 (News), 13:00 (Afternoon). Refresh a show folder’s contents, and the next time that slot fires, the new content is what goes out. No re-importing into a database, no re-publishing a playlist — just files on disk. Every fire is logged to Multilarm.playlist.log with the track name, which turns straight into a PRS/licensing return at the end of the month.
A short pre-recorded station ID is fine. But live shout-outs (“You’re listening to XYZ FM, ninety-two point one, the time is three oh seven”) date fast when pre-recorded. Multilarm’s built-in text-to-speech composes the ID on the fly from the pre-recorded word library in TTSPath. The schedule gets one slot per hour, with a parallel TextData entry for each:
<MAConfig><TextToSpeech>True</TextToSpeech></MAConfig>
<MAConfig><TTSPath>{app}/TTS</TTSPath></MAConfig>
<MAConfig><TTSFileFormat>mp3</TTSFileFormat></MAConfig>
<MAConfig><TextDelimiter>|</TextDelimiter></MAConfig>
<MAConfig><TextData>You're listening to XYZ FM, it's zero hundred hours.|You're listening to XYZ FM, it's nine hundred hours.|You're listening to XYZ FM, it's ten hundred hours.|You're listening to XYZ FM, it's thirteen hundred hours.</TextData></MAConfig>
Each TextData entry lines up one-for-one with its DateAndTimeData slot; the line for the 00:00 slot is spoken the moment the overnight show starts, etc. #TIMETOALARM+1# and other tokens can be embedded inside TextData if a countdown-style ID is wanted instead of the current hour label. Either way, the TTS word library is assembled once with Ctrl+R, and every future show ID speaks itself from disk without re-recording a single file.
Between scheduled shows, Multilarm’s virtual radio keeps the carrier alive with shuffled playback from a fallback music library. Set PlayRadio=Local, point RadioFolder at a well-stocked library, and the engine fades between tracks continuously, ducking automatically whenever a scheduled slot fires and returning afterwards:
<MAConfig><PlayRadio>Local</PlayRadio></MAConfig>
<MAConfig><RadioFolder>/srv/radio/Fallback/Music</RadioFolder></MAConfig>
<MAConfig><RadioFileFormat>mp3</RadioFileFormat></MAConfig>
<MAConfig><CrossfadeMs>2500</CrossfadeMs></MAConfig>
If a scheduled show is missing (folder empty because the producer hasn’t uploaded it yet), the startup audio health check flags it in yellow at boot; at run-time the slot simply no-ops and the virtual radio keeps playing. The listener hears continuous music instead of dead air, and the next scheduled slot takes over at its time. The status JSON field radio flips true whenever the virtual radio is on-air, so the station manager can see on the dashboard how often the fallback fills gaps — and go ask the producer why the show didn’t arrive.
Here is where the Cloud Relay pays for itself. The transmitter shed has no inbound ports — it sits behind a consumer NAT on a residential line. That used to mean presenters needed to either come on-site or SSH tunnel. Neither scales.
The Cloud Relay flips the direction of the relationship:
/api/poll.php on the station’s cheap shared hosting every five seconds./api/push.php every thirty seconds.End result: a presenter on a train can hit Skip, change the volume, transmit a voice insert, or swap the loaded config file via the live editor — and the only thing between their phone and the transmitter is a $4/month PHP host.
Voice Broadcast through the Cloud Relay uses the same dashboard as everything else. The presenter’s phone records on-device (HTTPS, so the browser allows the microphone), uploads a WAV file to /api/voice/upload, previews it privately if they want, and then hits Transmit. The transmitter plays the clip straight out of the FM output.
A 30-second top-of-hour birthday shout from a listener’s grandmother? Fine. A live update from a sports event the presenter is attending in person? Fine. All of it without dragging a mixing desk, an interface, or a laptop.
The dashboard’s status card is refreshed every fifteen seconds. The meaningful fields for a broadcaster:
playing — is audio on-air right now?radio — did we fall through to the internet radio feed?volumeProfile — is an unexpected cap currently clipping output?AlternateConfig variant is live (weekday / weekend / seasonal)?alarmInfo — what slot is currently playing, and what track did the engine pick?uptime — has the box been restarted unexpectedly?Four of those six are questions a traditional automation log can only answer after the fact. Multilarm answers them in near-real-time from anywhere with internet.
PlayRadio + RadioFolder) keeps the carrier on air between slots and when a show fails to land.Community radio has always been resourceful. Multilarm fits that temperament: one binary, one config, a few folders of audio, and a small PHP endpoint you can host for the price of a coffee. Try it on your next programming day.