Now Playing
Identifying the song in the room by tapping the audio DSP over AES67 and showing it on the bar's right-hand TV.
“What’s this song?” is the most common question a bartender gets and the worst one to answer mid-rush. I built a pipeline that listens to the room’s audio, identifies the track, and shows it on the bar’s right-hand TV within seconds. The next morning, a setlist page lists everything that played the night before.
In plain words. Think Shazam, built into the room. Instead of holding up a phone microphone, the system taps the digital audio feed straight from the sound system, works out which song it is, and puts the title on a TV and on a public web page.
At a glance
- Status: in production.
- What it listens to: a 6-channel network audio stream from the Q-SYS sound system, carrying three stereo inputs.
- Where the answer shows up: the right-hand bar TV, a public setlist page, and a few analytics views.
- Designed for: five to fifteen seconds between a song starting and its title appearing.
See it
You can try the live page at pub.ihnyc.affectivetech.com/setlist.
How it works
- The sound system sends a copy of its audio over the network.
- A Raspberry Pi turns short stretches of audio into a fingerprint and asks a song-identification service what it is.
- The answer goes to the pub’s backend, which updates the TV and the setlist page.
flowchart LR
Core[Q-SYS Core] -->|AES67 multicast, 6 channels| Pi[Raspberry Pi]
Pi -->|fingerprint on change| ID[Song-ID service]
ID --> Pi
Pi -->|HTTPS| W[Pub backend]
W --> TV[Right bar TV]
W --> SL[Setlist page and analytics]
Problems I had to solve
No single piece of software knows what is playing
Music enters the building from several places: the house feed, a bartender’s laptop, a guest DJ’s laptop. The Core sees all of them, so that is where to listen. The Core transmits one 6-channel multicast stream with three stereo pairs: the program feed, the wall stereo input, and the wall’s balanced (XLR) input. The pair that is loudest at the moment of identification becomes the track’s source label, so the setlist can show which input a song came from.
Where do you listen from?
I considered three ways to get audio out of the room:
- A script on the Core itself. Rejected: the Core has no internet route, and Lua is a poor fit for audio work.
- A USB audio interface on a spare analog output. Simple and cheap, but it adds a digital-to-analog-to-digital conversion, needs a free output, and cannot tell sources apart.
- AES67 from the Core. Q-SYS can transmit audio as a network stream natively, and the Core already sees every source, so tapping there beats tapping at the amplifier, which only sees the mix.
I chose the third. The amplifier’s control interface exposes metering, not audio, so asking the amp was never an option. Which song-identification service to use was a separate, swappable decision: it currently runs on a third-party service, with AcoustID and AudD available behind the same interface.
The audio network cannot reach the internet
The AV network has no direct route to the internet, so the Core and amplifier cannot call an identification service. The Pi sits on both networks and is the only crossing. A daemon on the Pi subscribes to the stream and exposes it as a 6-channel audio device. The Pi’s Ethernet port has no hardware timestamping, so the design has the Pi follow the Core’s clock using software timestamps.
Identification services charge per request
The room plays music continuously, so calls have to be kept to a few per song. A rolling buffer feeds a silence check and a Chromaprint fingerprint. A change detector compares the head of each fingerprint with the last one, and a hold-off timer plus a growing back-off after failed matches keep the number of identification calls small.
A device nobody thinks about has to survive power cuts
The Pi has no real-time clock, so its diagnostics refuse to timestamp anything until the system clock is confirmed as synchronized, then stamp held samples and events from a monotonic clock. Q-SYS event records are kept in a local database until the backend acknowledges them. A song result that cannot be sent is not retried; the next change simply identifies again.
Diagnostic threads sample temperature, undervoltage, Wi-Fi quality, and reachability of the router, the internet, and the backend every 30 seconds and report every minute. The admin combines these with each TV’s own heartbeats to explain why a screen went dark.
How I worked
I wrote the design first, including the rejected options above, and changed one variable at a time: the identification backend stayed fixed while the audio transport changed. The capture service also runs unchanged on a laptop against a local backend, so most of it could be tested before touching the pub’s Pi.
Tools
Q-SYS AES67, linuxptp, ALSA, Python, Chromaprint, Cloudflare Workers and D1.
Words used on this page
- AES67: a standard for sending studio-quality audio over an ordinary network.
- Multicast: one sender, many possible listeners, with no extra copies on the network.
- Fingerprint (Chromaprint): a compact signature of a few seconds of audio, used to look up a song.
- Hold-off and back-off: waiting before asking again, and waiting longer after each failed try.
- Setlist: the running list of songs that played.