voidcruiser-test.nl/content/rambles/mpd-and-icecast-on-nix.md

176 lines
5.9 KiB
Markdown

---
title: "Mpd and Icecast on Nix"
date: "2022-11-06T22:11:10+01:00"
author: "$HUMANOID"
tags: ["nix", "linux"]
description: "Ramble about setting up MPD + Icecast on NixOS"
---
# Introduction
For quite some time now, I've been wanting to move my home server from Debian to
NixOS. There were a few things that kept me from starting with the migration.
The main one being that the Debian installation still worked reasonably well. I
only had occasional hickups in regards to the two USB disks hooked up to my
Raspberry Pi 4. Every now and then they'd disappear. My guess is that it was due
to power draw as it would consistently happen when putting heavy IO loads on
either disk. One of these disks is home to my music collection and used by my
internet radio setup (in other words, the main thing I listen to when at home).
And this nicely brings my to my next issue.
{{< img class="stickers" src="/images/graphs/radio.png" mouse="Flowchart demonstrating software stack">}}
This radio consists of an MPD instance streaming to an Icecast2 instance,
being reverse proxied through an NGINX instance (future plans include managing
the music collection through Git Annex...). I have tried setting up MPD on NixOS
in the past, but couldn't get it to work for some odd reason. I don't remember
why I never managed it. I just know that I gave up after a few hours. Today I
decided to spend as much time as necessary to get at least the MPD + Icecast2
portion to work. The rest of this post will be detailing how I went about
setting configuring MPD + Icecast2 on NixOS as there were a few non-obvious
things I bumped into.
# Setting up MPD
To prevent my `/etc/nixos/configuration.nix` from ballooning into absurdity,
you'll want to create something like a `/etc/nixos/services/radio.nix` file
containing everything related to the radio stack. This file is then imported in
`/etc/nixos/configration.nix` in the `imports` section as follows:
```nix
{ config, pkgs, ... }: {
imports =
[ # Include the results of the hardware scan.
./hardware-configuration.nix
./drives.nix
./services/radio.nix
...
];
...
}
```
The first thing you'll want to do is enable the MPD service:
```nix
{ config, pkgs, ... }: {
services.mpd = {
enable = true;
...
```
From here, there are a few rather basic options that you'll want to set:
```nix
...
musicDirectory = "/path/to/music-collection/";
network.listenAddress = "any"; # not quite sure if this is needed
...
```
At the `extraConfig` section is where things get interesting. Here I assumed you
could point Nix to an existing config file with `(builtins.readFile
/path/to/mpd.conf)` but it seems I am wrong. It doesn't throw any errors when
switching to a new build, but it doesn't read the given config file either. I'm
probably missing something here. Please shout at me if you happen to know what
it is.
Regardless, you're going to want to add at least one audio output method. Since
we're going to make MPD talk to Icecast2, we're going to add `audio_output`
section of type "shout":
```nix
...
musicDirectory = "/path/to/music-collection/";
extraConfig = ''
audio_output {
type "shout"
encoder "vorbis" # FOSS codec for the win
name "My Shouty Stream"
host "<name found in the networking.hostName variable>"
port "8000" # or use whatever meme you like
mount "/mpd.ogg"
password "hunter2"
quality "5.0"
format "44100:16:2"
genre "Rythmic Noise!"
protocol "icecast2"
}
'';
};
...
```
Please don't take this snippet as gospel and read through the MPD configuration.
Whatever is found in `extraConfig` will be directly given to MPD as if through
`/etc/mpd.conf` on e.g. Debian. For instance, if you only locally want to listen
to your music through say PulseAudio, than you'll need a corresponding output
section.
{{< start-details summary="Example PulseAudio configuration I have on my laptop" >}}
```conf
...
audio_output {
type "pulse"
name "Pulse output"
audio_output_format "44100:16:2"
samplerate_converter "Medium Sinc Interpolator"
mixer_type "software"
replaygain "album"
volume_normalization "no"
}
...
```
{{< end-details >}}
## Mounting a music disk
In order to automount a disk on NixOS, add an expression something like the
following (indirectly) in your `/etc/nixos/configration.nix`:
```nix
fileSystems."/srv/music" = {
device = "/dev/sdb1"; # assuming that sdb1 is the partition with your music on it
# device = "/dev/disk/by-label/Sauserer"; # or ideally, if you have a labelled disk
fsType = "ext4"; # assuming it has a ext4 filesystem on it
};
```
I have my drives listed in `/etc/nixos/drives.nix` and import that in my
`configration.nix`
# Setting up Icecast2
The next step is to set up Icecast2. Same as with MPD, the first thing you'll
want to do is enable it:
```nix
...
services.icecast = {
enable = true;
extraConf = ''
<location>Floating around in an example configuration snippet on the internet</location>
<admin>icemaster@localhost</admin>
<authentication>
<source-password>hunter2</source-password>
<relay-password>even-more-super-secret-password</relay-password>
</authentication>
<limits>
<clients>100</clients>
<sources>2</sources>
<queue-size>524288</queue-size>
<client-timeout>30</client-timeout>
<header-timeout>15</header-timeout>
<source-timeout>10</source-timeout>
<burst-on-connect>1</burst-on-connect>
<burst-size>65535</burst-size>
</limits>
'';
hostname = "hostname:8000";
listen.port = 8000;
admin.password = "it gives me a headache that I can't get Nix to read this from a file";
};
}
```