Skip to content

oszuidwest/zwfm-encoder

Repository files navigation

ZuidWest FM Encoder

Audio streaming software for ZuidWest FM and Radio Rucphen. Stream audio from a Raspberry Pi to multiple SRT destinations simultaneously. Built for broadcast environments with real-time monitoring and web-based configuration.

Raspberry Pi and SRT logo

Features

  • Multi-output streaming - Send to multiple SRT servers with different codecs simultaneously
  • Real-time VU meters - Peak hold (1.5 s) with peak/RMS toggle, clip detection, updated via WebSocket
  • Silence detection - Alerts via webhook, email, file log, or Zabbix when audio drops below threshold
  • Web interface - Configure outputs, select audio input, monitor levels
  • Auto-recovery - Automatic reconnection with configurable retry limits per output
  • Multiple codecs - MP3, MP2, Ogg Vorbis, or uncompressed WAV per output
  • Update notifications - Alerts when new versions are available
  • Single binary - Web interface embedded, minimal runtime dependencies

Platform Support

Platform Status Audio Capture
Linux (Raspberry Pi) Primary arecord (ALSA)
macOS Development only FFmpeg (AVFoundation)
Windows Experimental FFmpeg (DirectShow)

Linux on Raspberry Pi is the primary target. macOS works for development. Windows is experimental.

Requirements

Installation

  1. Install Raspberry Pi OS Trixie Lite (64-bit)
  2. Configure HiFiBerry following the official guide
  3. Run the installer as root:
sudo su
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/oszuidwest/zwfm-encoder/main/deploy/install.sh)"

The web interface will be available at http://<raspberry-pi-ip>:8080

Default credentials: admin / encoder

Updating

Run the same installer script to update to the latest version:

sudo su
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/oszuidwest/zwfm-encoder/main/deploy/install.sh)"

The installer detects existing installations and asks whether to:

  • Update only - Downloads the latest binary while preserving your configuration
  • Fresh install - Overwrites configuration (existing config is backed up)

The web interface shows a notification when updates are available.

Audio Input

Connect the digital output of your audio processor to the HiFiBerry input.

Requirements:

  • 48 kHz sample rate
  • 16-bit depth
  • Stereo (2 channels)
  • S/PDIF format preferred (AES/EBU compatibility not guaranteed)

Codecs

Codec Encoder Bitrate Notes
MP3 libmp3lame 320 kbit/s
MP2 libtwolame 384 kbit/s Uses psymodel 4
Ogg libvorbis ~500 kbit/s (Q10)
WAV pcm_s16le Uncompressed

Silence Detection

Monitors audio levels and sends alerts when silence is detected or recovered. Uses hysteresis to prevent alert flapping:

Setting Default Range Description
Threshold -40 dB -60 to 0 Audio level below which silence is detected
Duration 15 s 1 to 300 Seconds of silence before alerting
Recovery 5 s 1 to 60 Seconds of audio before recovery

Alerting options (can use multiple simultaneously):

  • Webhook - POST request to a URL on silence start and recovery
  • Email - Microsoft Graph API notification to configured recipients on silence start and recovery
  • File Log - Append JSON Lines to a local file for each silence event
  • Zabbix - Send trapper items to a Zabbix server on silence start and recovery

Configure via the web interface under Settings → Alerts.

Microsoft 365 Email Setup

Email notifications use Microsoft Graph API with app-only authentication.

  1. Create an App Registration → copy Client ID and Tenant ID
  2. Add API permissions: Mail.Send (required), Application.Read.All (optional, for secret expiry warnings)
  3. Grant admin consent
  4. Create a client secret → copy the value immediately (won't be shown again)
  5. Create a shared mailbox as the sender (no license required)

The encoder warns when the secret expires within 30 days.

Zabbix Setup

  1. Import zabbix/template.xml in Zabbix (Data collectionTemplatesImport)
  2. Link the template to your encoder host
  3. Configure in the encoder: server, port (default 10051), host name (must match Zabbix exactly), and item key

The template creates triggers for SILENCE (Disaster), RECOVERY (Info), and TEST (Info) events.

Configuration

Configuration is stored in /etc/encoder/config.json on production systems. For development, use the -config flag to specify a custom path, or place config.json next to the binary.

{
  "system": { "port": 8080, "username": "admin", "password": "encoder" },
  "web": { "station_name": "ZuidWest FM" }
}

The installer creates a minimal config file. All other settings are configured through the web interface.

Event Log

The encoder logs all stream, silence, and recording events to a JSON Lines file for monitoring and debugging. Events are accessible via the web interface and REST API.

See docs/events.md for the complete event reference.

Architecture

flowchart LR
    A[Audio Input]

    subgraph Capture["Capture"]
        B[arecord]
    end

    subgraph Processing["Audio Processing"]
        D[Distributor]

        subgraph Metering["Metering"]
            M[RMS/Peak<br>Calculator]
            PH[Peak Hold<br>1.5s Decay]
            CD[Clip<br>Detect]
        end

        subgraph Silence["Silence Detection"]
            SD[Detector<br>Hysteresis]
            SDM[Dump Manager<br>Ring Buffer]
        end
    end

    subgraph Outputs["Streaming Outputs"]
        OM[Output Manager<br>Retry + Backoff]
        F1[FFmpeg MP3]
        F2[FFmpeg MP2]
        F3[FFmpeg OGG]
        S1[SRT 1]
        S2[SRT 2]
        S3[SRT 3]
    end

    subgraph Recording["Recording"]
        RM[Recording Manager]
        R1[Hourly]
        R2[On-Demand]
        ST[(Storage)]
    end

    subgraph Alerts["Alerting"]
        SN[Silence Notifier]
        N1[Webhook]
        N2[Email]
        N3[Log]
        N4[Zabbix]
    end

    subgraph UI["Web UI"]
        WS[WebSocket<br>10fps]
    end

    %% Main audio flow
    A ==>|S/PDIF| B ==>|PCM| D

    %% Metering branch
    D ==>|PCM| M -->|dB| PH
    M -->|samples| CD
    PH -->|dB JSON| WS
    CD -->|clip JSON| WS

    %% Silence detection branch
    D -->|dB| SD
    D ==>|PCM| SDM
    SD -->|events| SDM
    SD -->|state JSON| WS

    %% Alerting
    SD -->|event| SN
    SDM -->|MP3| SN
    SN -->|HTTP| N1
    SN -->|Graph API| N2
    SN -->|file| N3
    SN -->|trapper| N4

    %% Streaming
    D ==>|PCM| OM
    OM ==>|PCM| F1 -->|SRT| S1
    OM ==>|PCM| F2 -->|SRT| S2
    OM ==>|PCM| F3 -->|SRT| S3

    %% Recording
    D ==>|PCM| RM
    RM ==>|PCM| R1 -->|MP3| ST
    RM ==>|PCM| R2 -->|MP3| ST
Loading

Audio Flow

  1. Capture: arecord (Linux) or FFmpeg (macOS/Windows) captures 48kHz 16-bit stereo PCM
  2. Distributor: Processes PCM in ~100ms chunks, fans out to all consumers
  3. Metering: Calculates RMS/peak levels in Go (no FFmpeg filters), holds peaks for 1.5s, detects clipping at ±32760
  4. Silence Detection: Hysteresis-based detection with configurable threshold/duration/recovery. Buffers 15s audio context before/after silence events
  5. Alerting: Silence triggers webhook, email (MS Graph), log (JSON Lines), and/or Zabbix. Recovery includes MP3 dump attachment
  6. Streaming: Per-output FFmpeg processes with automatic retry and exponential backoff
  7. Recording: Hourly rotation or on-demand, with optional S3 upload

Post-installation

Optional hardening:

echo "dtoverlay=disable-wifi" >> /boot/firmware/config.txt  # Disable WiFi
apt remove bolt bluez ntfs-3g telnet                        # Remove unused packages

SRT Resources

Related

License

MIT License - See LICENSE.md

About

Software to turn a Raspberry Pi into a production grade audio encoder

Topics

Resources

License

Stars

Watchers

Forks

Contributors 5