From c2ce6c19f9fee8b6d6ffe333ba2afdcfd675ff0a Mon Sep 17 00:00:00 2001 From: Marty Sluijtman Date: Sun, 6 Nov 2022 23:50:14 +0100 Subject: [PATCH] Start of icecast mpd nix article and cleanup --- content/rambles/mpd-and-icecast-on-nix.md | 175 ++++++++++++++++++++++ content/rambles/windowmanagers.md | 4 +- static/images/graphs/radio.png | Bin 0 -> 14993 bytes themes/vugo | 2 +- 4 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 content/rambles/mpd-and-icecast-on-nix.md create mode 100644 static/images/graphs/radio.png diff --git a/content/rambles/mpd-and-icecast-on-nix.md b/content/rambles/mpd-and-icecast-on-nix.md new file mode 100644 index 0000000..753201d --- /dev/null +++ b/content/rambles/mpd-and-icecast-on-nix.md @@ -0,0 +1,175 @@ +--- +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 "" + 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 = '' + Floating around in an example configuration snippet on the internet + icemaster@localhost + + hunter2 + even-more-super-secret-password + + + + 100 + 2 + 524288 + 30 + 15 + 10 + 1 + 65535 + + ''; + 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"; + }; +} +``` diff --git a/content/rambles/windowmanagers.md b/content/rambles/windowmanagers.md index e42bd11..007f8b3 100644 --- a/content/rambles/windowmanagers.md +++ b/content/rambles/windowmanagers.md @@ -63,7 +63,7 @@ I ran DWM as my main window manger for over a year before having issues with some fullscreen applications and the JetBrains suite, which I had to use for college activities. -{{< img class="stickers" link="https://xkcd.com/1806/" src="/images/xkcd/borrow_your_laptop.png" >}} +{{< img link="https://xkcd.com/1806/" src="/images/xkcd/borrow_your_laptop.png" >}} I decided to give AwesomeWM another shot after having figured out what I want from a tiling window manger. This time I managed to get something that worked @@ -113,7 +113,7 @@ and I decided to dive into learning the language with the goal of being able to fully understand my monstrous 384 line config file (586 lines including the documentation in commented sections). -{{< img class="stickers" src="/images/config_length.png" >}} +{{< img src="/images/config_length.png" >}} I don't fully understand it yet at the time of writing, but I do understand it a hell of a lot better than when I produced most of those 300 lines. diff --git a/static/images/graphs/radio.png b/static/images/graphs/radio.png new file mode 100644 index 0000000000000000000000000000000000000000..f5ce12d60c4630efa3fcea457137fba97e6179df GIT binary patch literal 14993 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