Streaming audio server with a Raspberry Pi 5 and Mopidy – Never Mind

Well, this is a bummer. The Iris interface on Mopidy is nice, and when I’m doing everything manually, it works fine. BUT, I wanted to do things with scripting. I can somewhat get things to work, but not reliably. So I’m going to give up on Mopidy and go back to MPD (Music Player Demon).

I’d managed to build a little routine that, once an hour, generates a sound file with the current time in it. I copied it to the right place, updated the file cache so that the new file is the current one, and then threw (using CURL) the command to add the file to the Mopidy queue. For testing, I had cron run it every two minutes.

# Add the current_time.mp3 file to the queue

/usr/bin/curl -s -X POST "$MOPIDY_URL" \
-H "Content-Type: application/json" \
-d '{
"method": "core.tracklist.add",
"params": {
"uris": ["'"$FILE_URI"'"],
"at_position": 1
},
"id": 1,
"jsonrpc": "2.0"
}'

But, if the queue is empty, Mopidy isn’t going to play anything. So then I had to add:

# Start playback

/usr/bin/curl -s -X POST "$MOPIDY_URL" \
-H "Content-Type: application/json" \
-d '{
"method": "core.playback.play",
"id": 2,
"jsonrpc": "2.0"
}'

And now I’ve three problems.

The first is that there is a weird loop problem where once: the file plays correctly. I’ll hear “The current time is 16:40.” The next time the cron job runs the bash script, I’ll hear “The current time is 16:42. The current time is 16:42.” The third time it runs, I’ll hear “The current time is 16:44. The current time is 16:44. The curren” and the playback is interrupted.

It is always in triples; 16:46 would play fine, 16:48 would play twice, and 16:50 would attempt to play three times but get cut off.

So that is one problem. The next is that I prefer to do my scripting in Perl, and I know that Perl has been doing JSON-RPC for a really long time now. But the Perl script I wrote doesn’t seem to be connecting to the Mopidy server. It tells me that it connected, and shows me a result (instead of a refusal to connect), but the Mopidy debug log does not acknowledge that a connection was made.

The third is that even when I add a song manually using the Iris interface, that “core.playback.play” method would break the playback of the currently playing song. The idea was that if I only create a current_time.mp3 file at the top of the hour, and add it to the queue, then within a few minutes, I’ll hear was hour it is. But now I’ve got the worst of both worlds: if the queue was empty, no playback of the current_time.mp3, but if I trigger the playback, the current song gets clobbered. Sigh.

I really liked that Mopidy has the nice Iris web client; but, if the only way to interact with the server is via mouse and keyboard, well then I’m out.

Streaming audio server with a Raspberry Pi 5 and Mopidy

My first attempt at an MPD streaming box was a tough config, because I was learning along the way, hitting every stumbling block there is. Getting the Music Player Demon running wasn’t terrible, but getting my various client machines to listen to the streaming server was tough. Okay, I am going to try again, and hopefully, it actually works with Mopidy this time.

The Iris plugin to Mopidy is nice. Previously I used Cantata, which was fine, but it was a local install on each machine. Iris is good because it runs on the server, and I don’t need to do a local install on each machine just to control the music stream.

On to the build!


I used the Raspberry Pi Imager program to put the Raspberry Pi OS Lite (64-bit) on the SD card. Then I booted the device, ssh’d in and ran

sudo apt update

followed by

sudo apt upgrade -y

followed by a sudo reboot now

sudo raspi-config

I’m changing the hostname. Also, because I prefer vim to nano:

sudo apt install vim -y
sudo update-alternatives --config editor

And of course, now I get to install my favorite aliases and history search keystrokes. These are detailed here.

Next, I’m going to follow the instructions at Mopidy Installation

I am following the section Install from apt.mopidy.com:

sudo mkdir -p /etc/apt/keyrings
sudo wget -q -O /etc/apt/keyrings/mopidy-archive-keyring.gpg https://apt.mopidy.com/mopidy.gpg
sudo wget -q -O /etc/apt/sources.list.d/mopidy.list https://apt.mopidy.com/bookworm.list

So one difference is that the instructions say to wget from https://apt.mopidy.com/bullseye.list but this version of Debian on the Raspberry Pi is bookworm (Debian 12 instead of 11). We’ll find out if I just shot myself in the foot. 😉

sudo apt update
sudo apt install mopidy -y
sudo apt install mopidy-mpd -y
sudo apt install mopidy-local -y
sudo apt install snapserver snapclient -y
sudo apt install python3-pip -y
sudo python3 -m pip install Mopidy-Iris --break-system-packages

The instructions I found say to simply do sudo python3 -m pip install Mopidy-Iris but Debian (or perhaps it is Python) barks at me with “error: externally-managed-environment – This environment is externally managed” and some potential way around the problem. That’s a nope: I want to run as a systemd service, and isolating stuff into unreachable environments is not the battle I wish to fight today.

And another thing: the Snapcast instructions assume that Snapweb is installed, but it never is. First, I downloaded it from https://github.com/badaix/snapweb/releases and picked the file snapweb.zip

Then I copied it to the Mopidy server, and did this:

sudo mkdir -p /usr/share/snapweb
sudo unzip snapweb.zip -d /usr/share/snapweb

Okay, I’m almost ready to start configuring things.

Later on down the line, the mopidy.conf file will consider that the files to serve up are at a certain location. What location? Here’s how to find out:

sudo mopidyctl config

and look in the [local] section for media_dir = /var/lib/mopidy/media

Now would be a great time to mount my music files via a CIFS / SMB share:

sudo apt install cifs-utils -y
touch /root/credentials.smb
vim /root/credentials.smb
username=epstein_did_not_kill_himself
password=Apparently-Child-Rape-Is-Okay-If-The-Deep-State-Does-It,-Because-With-The-Deep-State,-The-Ends-(Blackmail)-Justify-The-Means-(Child-Rape).--Everyone-Who-Is-Human-Recognizes-The-Evil,-But-The-People-Who-Make-Up-The-Deep-State-Are-Sociopaths.--Unfortunately,-The-People-In-The-Current-Administration-Who-Promised-To-Drain-The-Swamp-Have-Been-Broken,-Most-Likely-With-Threats-To-Their-Family.--What-A-World,-Amirite?
sudo chmod 600 /root/credentials.smb

Then we can edit /etc/fstab

//hostname/smbsharename/data /var/lib/mopidy/media cifs vers=3.0,credentials=/root/credentials.smb,_netdev,iocharset=utf8 0 0

I’m also going to try to keep from wearing out the SD card so quickly, so I’ll try this:

tmpfs   /var/cache/mopidy   tmpfs   size=1G,mode=1777   0 0

Okay, things I’ve changed in /etc/mopidy/mopidy.conf :

[http]
hostname = ::

[mpd]
hostname = ::

This was previously hostname = 127.0.0.1

I changed to listen on all interfaces (that’s what :: does) because I do want any of my machines on my local network to be able to interact with the Mopidy server. Note that I am planning on never putting this box on the public Internet.

There is another change I made in /etc/mopidy/mopidy.conf :


output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! filesink location=/tmp/snapfifo

Then I get to edit /etc/snapserver.conf:

sudo vim /etc/snapserver.conf
source = pipe:///tmp/snapfifo?name=Mopidy

I’m going to try to make sure Snapcast loads first, before Mopidy, because Snapcast needs to be writing into /tmp/snapfifo before Mopidy starts reading it.

sudo systemctl edit mopidy.service
[Unit]
After=snapserver.service
Requires=snapserver.service

To configure the Snapweb server:

sudo vim /etc/snapserver.conf

Find the line #doc_root = /usr/share/snapserver/snapweb and change it to:

doc_root = /usr/share/snapweb

And I think I am at the moment of truth:

sudo systemctl enable snapserver.service
sudo systemctl enable mopidy.service

sudo systemctl restart snapserver.service
sudo systemctl restart mopidy.service

If things are looking correctly, my next step is….

sudo mopidyctl local scan

Well, it almost worked right on the first try. I also needed to do this:

echo "fs.protected_fifos = 0" | sudo tee /etc/sysctl.d/snapcast-unprotect-fifo.conf
sudo sysctl --system

Hooray! My streaming music server is up and running within my local network. This was fun. 🙂

Home alarm clock update – now with streaming audio

As mentioned in Home alarm clock update, I’d like to work with Snapcast.

Well, right off the bat, all the instructions for getting the Snapcast client to work automatically, did not. When I say “automatically” I mean that after I reboot the machine, I simply want the Snapcast client running without me having to do anything else. There were suggestions about making it a system service, and a user service, and none of those worked. I’m pretty sure it has something to do with my logged-in user having an environment which is different from what systemd or cron sees.

KAlarm to the rescue!

It has an option to launch stuff after reboot. That’s what I needed.

snapclient --host <IP address of MPD server goes here>

It does take a few seconds after reboot for KAlarm to figure out to run this command. But as soon as it does, my machine (whichever machine) taps into the stream, and music starts playing out. But because KAlarm doesn’t launch until after everything in KDE is up and running, I’m not having these weird errors where the Snapcast client cannot see the stream or the audio devices to play it out.

This is great.

However, what about my alarms? Those are music files too (well, sometimes a TTS wave file). The multiplexing nature of computer audio would have the two playing simultaneously. That is less than ideal.

Turns out that VLC has an option for exiting nicely after playing a file. Add a couple of MPC commands, and we’re golden. The magic command for VLC is rc --play-and-exit

I did have to install the MPC client (for controlling MPD servers) on my machines.

But now my KAlarm commands look like this:

mpc --host <IP address> pause
vlc --intf rc --play-and-exit /path/to/friday_morning.pls
mpc --host <IP address> play

I can queue up a whole stream of music files as background music, using Cantata, and when it comes time for my alarm to fire, to let me know it is time for the next event, the background music pauses through the whole house, the alarm does its thing, and then the background music resumes.

This is so neat. I am having fun with my computers again. 🙂

And I enjoy hearing the London Philharmonic Orchestra playing Sonic the Hedgehog: a Symphonic Suite and Elder Scrolls – Skyrim: Far Horizons. The Elder Scrolls V: Skyrim: Original Game Soundtrack has some great orchestral music. And now my whole house is filled with it.

A Future Me email from six months ago, and the situation has not gotten any better (Linode is in decline)

Dear FutureMe,

I am sad that Linode appears to be going to hell. It got acquired by Akamai, and now my personal email server is getting blocked by Microsoft and Comcast for being in a datacenter which is blasting out spam. I haven’t been able to reply to Carol Ann’s emails for five days. My support ticket with Linode has been open and sitting there with no action. I checked my outbound mail log, and it isn’t me who is blasting out spam. I presume that Akamai has opened their doors to be spammer-friendly because spammers have $$$ to spend with them.

I think that I need to ditch Linode and move to some VPS which hasn’t gone through enshitification.

I’m bummed because for the 501(c)(3) I’m a member of, I was the one who made the pitch to move off our previous VPS and to Linode (for a slight increase in cost).

Last time I moved mail servers, it was a chore.