Initially, the idea was pretty simple: Have a dedicated raspberry pi to manage and play some pre-configured streams. I had written a simple web interface that controlled a Music Player Daemon instance, plugged a powered speaker into it, and all was well. Until I decided I wanted it to look more like a real radio, that is.
I ended up finding a beat-up, non-functioning Regency Monitoradio (circa 1956) at a flea market. The plan was to gut it, clean it up, and stick the pi and speaker in it.
Before I even got it home, scope-creep set in. I thought it would be cool to control the power to the pi with the original radio controls. Since I would already need to hook it up to the on/off/volume potentiometer for that, I might as well see if I could get the volume knob to work. And I might as well see if I could simulate using the tuner knob to choose stations while I'm at it.
I ended up deciding to try to keep as much of the feel of the existing controls as I could. So I figured I'd attempt use the original 60+ year old potentiometers for the volume and squelch. I cleaned them up as good as I could and connected them to the pi. Surprisingly, they worked well enough. Polling them would produce mostly consistent results, but every so often, a reading would be far from the actual value. I decided I'd try to mitigate this in the software, and stuck with the original pots, which are still in place today.
The tuner was more interesting. The tuning shaft was broken, and the string had fallen off.
Fixing the shaft was easy. Figuring out how to wind the string around everything was a bit of a puzzle. After tracing a bunch of different paths, I "wound" up with this.
With the string back on, I needed to figure out how to connect a new potentiometer to the original tuning shaft. I ended up buying a potentiometer that had its own shaft, then connecting it to the existing radio's shaft with a coupler, so that turning the radio shaft ends up turning the potentiometer.
With all 3 potentiometers hooked up, I got two white LED lights to illuminate the tuner area and connected them to the pi. With that, everything was connected.
The backend is a spring boot application, initially written in groovy, but then converted to Kotlin at some point along the way. It scans the potentiometers for changes and acts on them and controls a Music Player Daemon instance for all audio functionality. It also provides a RESTful API so that the webapp can configure and control the radio as well.
I wanted to make it so turning the tuner made it behave like tuning an actual radio, so I'd need to simulate what it was like moving the knob, listening to static, and then honing in on a station. Stations would be recognized at certain potentiometer values and when outside those values, rather than having MPD play the radio stream, it would play static. Here's a video demo of the tuner and the volume knob:
I expected this one to be simple: When the value of the volume potentiometer changed, pass the new value along to the MPD instance, and that would be that.
But what I found when playing with it was that the volume didn't seem to increase naturally when turning the knob. After logging the potentiometer values and watching them change as I turned the pot, I realized that the volume potentiometer was producing logarithmic values, as this seems more natural to the human ear as volume increases. The issue is that software like MPD already takes this into account. If you pass it values that increase linearly, it's already internally adjusting it logarithmically. So in this case, the logarithmic adjustment was happening twice.
I looked for ways to ask MPD to not do this, and apparently there are some ways that involve bypassing MPD's volume controls and routing them elsewhere. As I only wanted the application to have to deal with MPD for all audio, I decided to just deal with this in software, basically just performing a calculation on the values coming from the volume pot to make read linearly, then pass that value to the MPD instance.
Well, there is no squelch needed in this simulation. Squelch basically acted like a gate to keep noise down back in the day. So, I figured I'd repurpose it for something useful in this scenario. After toying around with a few different ideas, I ended up deciding it would be cool to use it to announce the currently playing artist, song, and station. Rather than use one of the "big boys" for doing text-to-speech synthesis, I went with Voice RSS. When the squelch pot is rotated, it fires off an HTTP request to VoiceRSS with the text of the current artist, song, and station, and plays what is returned from their API. Here's a video demo:
In addition to the backend software for handling the radio controls, I wanted to keep a web interface for configuring it. It's written in React/MUI and supports configuring stations, calibrating the tuner knob, and a few other things.
Radio streams come and go - a lot. Often, the station still exists but their streaming technology changes. Some of the smaller stations just go away for good, but new ones still pop up all the time. And, some streams work in the context of a web browser but not as a true audio stream that MPD can handle. This is becoming more and more prevalent, as radio stations are forcing video ads in their streams -- yes, a video ad in an audio stream. I'd be surprised if anyone streaming a radio station in their browser is actually paying attention to that tab/window other than listening to it, but I've never been a marketing guy. Anyway, to keep on top of this, I built an area where I can test new streams to see if they work (and if I like them), before adding them:
In addition to a place for configuring stations, I also wanted a way to tune to a station without physically reaching the radio. Yea, this goes against some of the principles I've outlined, but I tend to look at it as the best of both worlds: