Connected Home

How To: Play HD Radio on Your Mac

Overview

Most radio stations stream online, though often not in high fidelity. HD Radio subchannels, the specialty programming that makes the technology genuinely interesting, are almost never streamed at all. So, if you want to actually hear these broadcasts, you’ll need to tune in the over the air broadcast. A $30 USB dongle, some open-source software, and a laptop are all it takes, making this a fun project to learn about software-defined radio (SDR) and signal processing. What follows is a step-by-step guide to decoding a live HD Radio broadcast on a Mac and playing the audio through your computer speakers.

Digital Radio: Europe vs. the United States

Digital terrestrial radio broadcasting arrived in Europe well before it did in the United States, and the two continents took fundamentally different paths. Europe standardized on DAB+, an open ITU/ETSI/WorldDAB standard based on the MPEG-4 HE-AAC audio codec. It has been widely adopted across more than thirty countries and offers efficient spectrum use, strong error correction, and multi-channel broadcasting on a single frequency block.

In the United States, terrestrial digital radio uses an in-band-on-channel (IBOC) approach: the digital signal is transmitted alongside the existing analog signal on the same frequency. This is based on a proprietary implementation by Xperi, trademarked as “HD Radio.” (The “HD” stands for Hybrid Digital, not High Definition.) A station’s analog signal is simulcast on HD1. Many stations broadcast secondary channels (HD2 or HD3) with niche or specialty programming. Some of the HD Radio specification is documented as part of the NRSC-5 standard. In July 2017, a researcher reverse-engineered and publicly documented the remaining proprietary details including the HE-AAC audio compression and released a working open-source software decoder.

HD Radio: Adoption and Availability

HD Radio has achieved meaningful penetration in the U.S. broadcast ecosystem. Roughly 2,500 American stations now broadcast an HD signal, and HD Radio tuners are present in approximately half of all new cars sold in the United States. While listening in your car is straightforward, at home, it is a different story. Today, no major AV receiver manufacturer includes HD Radio in their current lineup. The only dedicated standalone home tuner that remains readily available is the Sangean HDT-20, which connects to an existing stereo receiver via analog RCA or optical/coaxial digital output. If you want HD Radio in your home today, you either track down used hardware or build something yourself.

DIY HD Radio on Your Mac

Software Defined Radio

Software defined radio (SDR) replaces dedicated hardware radio circuits with general-purpose computing. Instead of a fixed-function chip tuned to a specific modulation scheme, an SDR receiver digitizes a wide swath of the radio spectrum and hands the raw data to software, which performs all the filtering, demodulation, and decoding. This means a single piece of hardware can receive AM, FM, HD Radio, aircraft transponders, weather satellites, GPS, and dozens of other signal types purely by changing the software.

The GNU Radio project is the most comprehensive open-source SDR toolkit, providing filters, channel codes, equalizers, demodulators, vocoders, and decoder blocks. For this project we use a simpler, purpose-built tool, but if the underlying technology catches your interest, GNU Radio is the natural next step.

RTL-SDR Hardware Adapter

Low-cost DVB-T USB dongles based on the Realtek RTL2832U chipset are the standard entry point for SDR hobbyists. Originally designed to receive European digital TV, they were discovered to expose raw IQ data that can be processed as a general-purpose radio receiver.

When I originally wrote this article in 2017, I recommended the RTL-SDR Blog V3 Bundle. The current version, the RTL-SDR Blog V4 Bundle (~$30 on Amazon), is the upgrade to get today. It uses the improved R828D tuner for better sensitivity and a lower noise floor, includes a temperature-compensated 1PPM TCXO oscillator for significantly better frequency stability, and supports HF reception down to 500 kHz without an external upconverter. For our FM HD Radio use case the stability improvement alone is worth it; the V4 locks onto HD Radio signals noticeably more reliably than older clone dongles.
IMG_5033

A Better Antenna

If you live in the suburbs, you may find that the small whip antenna included in the RTL-SDR kit is insufficient for reliable HD Radio lock. Your best option is an outdoor or attic-mounted FM antenna. If that is impractical, the Terk Amplified AM/FM Stereo Indoor Amplified Antenna (~$25) works well indoors.

Software for Your Mac

NRSC5 Radio Tuner

The open-source NRSC-5 decoder from the original researchers has been actively maintained since their 2017 release. It has grown substantially: gain control is now automatic by default, log-level control is more granular (-l replaces the old debug flag approach), WAV is the default output format, and the decoder exposes both a C API and a Python API for building applications on top of it. A graphical front-end, NRSC5-DUI, is also available if you prefer a point-and-click interface that displays album art, traffic maps, and weather radar from stations that broadcast them.

Because nrsc5 is not in Homebrew’s standard formula catalog, you need to build it from source via cmake:

# Install dependencies
brew install cmake autoconf automake libtool pkgconf libao fftw rtl-sdr

# Clone and build
git clone https://github.com/theori-io/nrsc5.git
cd nrsc5
mkdir build && cd build
cmake ..
make -j$(sysctl -n hw.logicalcpu)
sudo make install

Audio Player

There are a variety of audio player applications that can handle piped audio, including SOX, MPV, and MPLAYER. I recommend using MPV, which you can install via homebrew

$ brew install mpv

Finding HD Radio Stations in Your Area

HD Radio provides a useful station directory by zip code here. Alternatively, you can use the Radio Locator signal finder here.

Playing an HD Radio Station

Select a station you want to receive. For our example, I use WHYY-HD1 in Philadelphia. You need to specify the center frequency and the HD channel number. The channel argument is zero-indexed: HD1 is 0, HD2 is 1, HD3 is 2, and so on. The -q flag disables log output; omit it if you want to see signal-quality diagnostics while the decoder is locking on.

Pipe audio from nrsc5 to mpv as follows:

nrsc5 -o - -q 90.9 0 | mpv -

To receive a secondary HD channel — such as WXPN’s XPN2 on 88.5 MHz HD2 — increment the channel argument:

nrsc5 -o - -q 88.5 1 | mpv -

After a brief lock-on period (typically 5–15 seconds with a good signal), you should hear the station through your Mac’s speakers. The -o - flag writes WAV audio to stdout, which is the default output format; mpv reads it from stdin via the - argument. Using WAV ensures that nrsc5 — not the audio player — decodes the HE-AAC SBR audio, which matters because not all players handle HD Radio’s AAC extensions correctly.

If you want to see the signal-quality indicators as the decoder runs — useful to show kids what locking onto a digital signal actually looks like — simply drop the -q flag:

nrsc5 -o - 90.9 0 | mpv -

Using a Terminal Script for Advanced Audio Effects

I use the following script to simplify tuning radio stations. All you need to do is create a shell file and pass the frequency and band. For example, “zsh HDplay.zsh 90.9 0”. If a frequency isn’t specified, it will default to 90.9Mhz. Similarly, if a band isn’t specified, it will default to HD1. You can add additional SOX or MPV+FFMPEG effects globally, such as resampling, loudness, and dithering filters. I continue to tweak these settings to obtain a less harsh “digital” sound and to widen the stereo separation. nrsc5 outputs Program Service Data (PSD) — including title, artist, and genre — to stderr as each track changes. By merging stderr into the pipeline with 2>&1 and filtering with grep, you can display this metadata in your terminal while audio continues playing uninterrupted.

HDplay.zsh

#!/usr/bin/env zsh
# HDplay.zsh — Stream an HD Radio station via nrsc5 + mpv
# Usage: HDplay.zsh [FREQUENCY] [BAND]
#   FREQUENCY  FM frequency in MHz (default: 90.9)
#   BAND       HD subchannel, 0-indexed: 0=HD1, 1=HD2 (default: 0)
# Example: HDplay.zsh 88.5 1   → WXPN XPN2

emulate -LR zsh
setopt errexit nounset pipefail

# ── Dependency check ─────────────────────────────────────────────────────────
for cmd in nrsc5 mpv; do
  if (( ! $+commands[$cmd] )); then
    print -u2 "Error: \"${cmd}\" is not installed. Run: brew install ${cmd}"
    exit 1
  fi
done

# ── Argument defaults ─────────────────────────────────────────────────────────
local FREQUENCY="${1:-90.9}"
local BAND="${2:-0}"
local HDBAND=$(( BAND + 1 ))

# ── Basic input validation ────────────────────────────────────────────────────
if [[ ! "$FREQUENCY" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
  print -u2 "Error: invalid frequency \"${FREQUENCY}\""
  exit 1
fi

if [[ ! "$BAND" =~ ^[0-3]$ ]]; then
  print -u2 "Error: band must be 0–3 (HD1–HD4), got \"${BAND}\""
  exit 1
fi

# ── Tune ──────────────────────────────────────────────────────────────────────
clear
print -P "%B%F{white}%K{blue} Tuning to ${FREQUENCY} MHz HD${HDBAND} %k%f%b\n"
print "Metadata will appear below as track info is received."
print "${(l:50::─:)}"

# Pipe nrsc5 WAV output to mpv; merge stderr so grep can display PSD metadata
# while mpv continues to receive clean audio on stdin.
nrsc5 -o - -q -f wav "$FREQUENCY" "$BAND" 2>&1 \
  | tee >(grep --line-buffered -E "(Title|Artist|Genre|Station):" >&2) \
  | mpv - \
      --audio-samplerate=48000 \
      -af lavfi='extrastereo' \
      --no-terminal