DIY internet radio

From Helpful
Revision as of 15:41, 15 June 2021 by Helpful (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
the result

An easy choice of one's own music in a living room is nice. We used to use our smart TV, but when its app store and support stopped existing it became a dumb TV again.

I wanted to make something easier to use, and unobtrusive.



Interface

Made of this, the display holder shown below, some wire, hot snot, a box, and a power supply


I wanted something as basic as possible.

This is why it's

  • a pushbutton to change station and turn off
short press goes to next station
long press turns it off
  • a SSD1306 style OLED display
in part because it's small and cheap, in part because I think it's more elegant that it makes no light when off
showing current station, and whether we're connecting or playing
Making the hole for the display look less rough


Physically

It's

  • a box holding the raspberry
  • a piece of nice looking laminate flooring in front.
    • holding the button
    • and the display - with a 3D printed

I should probably countersink the screws a little better, but I'm sort of fond of the DIY look.




Platform and software

I considered an ESP8266 or ESP32, also for the sake of learning how to abuse it for this, which shouldn't be hard since (other people have already made this), but I figured its PWM-based audio would probably not be great, and the point was nice music.

So I settled on a Raspberry Pi (which is still PWM-based, but faster and probably more regular), and that platform also makes it a little easier to deal with changing changing stations and code by SSHing in, dealing with less-usual stations in code, and I had some worry about TLS though most stations don't put their stream on that.

Also it already has an audio jack. Don't make projects harder than they need to be, y'know.


Software

There are various ready-made things to do web radio and more.

Those either leave a lot to be duct taped together (MPD+controller interface)
or are too fancy, e.g. giving an interface much more complicated than I need (e.g. volumio)


I had some practical worries. Say, some radio stations change their stream URL over time. That's easy enough to work around with a few lines of screen scraping code, not so much if all you get is "add shoutcast URL here".

But more importantly, integrating that display and button into anything would be as hard as writing my own.


That, and given that the functionality is so very basic, I wrote my own.

It's a ~200-line, quick and dirty python script that mostly just runs ffplay,

does a
killall ffplay
before switching to a new station, and determines whether something is playing by using
lsof
to see if something has an ALSA device open. I'm not sure it's even worth cleaning up.


It relies on CircuitPython for the button (GPIO interface) and display (I2C, and existing SSD1306 library), and ffmpeg for playing.


I have half a dozen stations in there, like FIP, Shirley and Spinoza, and some some coming from my own music collection.


One's own music as web radio

One easy-enough way way to put your own music on a standard shoutcast-style stream to point a standard internet radio player at is ([little more on how those work here]) is a little server that runs:


LiquidSoap is a flexible tool (to the point it's a little hard to get started with), but I use it mainly to read a playlist.

After some experimentation, each station amounts to a configuration like

out = output.icecast( %mp3(bitrate=160, samplerate=44100, stereo=true), 
                      host="localhost", password="hackme",  name='piano')
 
list    = playlist( mode='randomize', '/Music/ice_play/piano.m3u8' )
default = single(   "/etc/liquidsoap/Silence.ogg" )
s       = fallback( [list,default] )
# TODO: figure out replaygain's rough edges
skys    = sky(rgs)   # multiband compression, makes some sense for quiet listening
#rgs = crossfade(start_next=2.,fade_out=2.,fade_in=2.,rgs)
 
out(   mount="piano",   add( [ skys ] )   )


One downside to this approach is that this radio is always running, i.e. each channel always runs its encoder , each using maybe ~10% of a CPU core, regardless of whether anyone is listening.

There are ways around that - by not using shoutcast, and writing a bunch of custom code. So I didn't do that yet.


See also