The Complete Computing Environment

Listening to Music with MPD

LifeTechEmacsTopicsArcology

Listening To Music is something I do a fair lot, and I remain displeased with the clients and interfaces at my disposal. Currently, I use MPD, the Music Playing Daemon which is a local server and a set of clients. On My Laptop where i have Emacs I use a few different clients for different usecases, each one well works for different uses.

MPD and Client Setup

{ config, pkgs, ... }:

{
  services.mpd = {
    enable = true;
    musicDirectory = "/home/rrix/Music";
    playlistDirectory = "/home/rrix/Music/playlists";
    dataDir = "/home/rrix/Music";
    network.listenAddress = "any";
    extraConfig = ''
    audio_output {
        type                    "pulse"
        name                    "My Pulse Output"
    }
    audio_output {
        type                    "fifo"
        name                    "my_fifo"
        path                    "/tmp/mpd.fifo"
        format                  "44100:16:2"
    }
  '';
  };
  services.mpd-discord-rpc = {
    enable = true;
    settings = {
      hosts = [ "localhost:6600" "fontkeming:6600" ];
      format = {
        details = "$title";
        state = "On $album by $artist";
      };
    };
  };
  services.mpdris2.enable = true;

  programs.ncmpcpp = {
    enable = true;
    settings = {
      visualizer_data_source = "/tmp/mpd.fifo";
      visualizer_output_name = "my_fifo";
      visualizer_in_stereo = "yes";
      visualizer_type = "spectrum";
      visualizer_look = "+|";
    };
    bindings = [
      { key = "j"; command = "scroll_down"; }
      { key = "k"; command = "scroll_up"; }
      { key = "J"; command = [ "select_item" "scroll_down" ]; }
      { key = "K"; command = [ "select_item" "scroll_up" ]; }
    ];
    package = pkgs.ncmpcpp.override { visualizerSupport = true; };
  };

  home.packages = [ pkgs.mpc_cli pkgs.cantata ];
}
(use-package mingus)

Mopidy

Mopidy is an extensible music server written in Python. It's mostly designed to provide an easy hardware jukebox for Spotify or whatever crappy web service you want to use. I use it for Bandcamp, mainly. There is NixOS module support for it so I enable it in My NixOS configuration. I want to use it for Youtube Music but this doesn't work reliably because they're fucking cops, though…

{ pkgs, ...}:
let
  mopidy-bandcamp = pkgs.callPackage ../pkgs/mopidy-bandcamp.nix {};
  extPkgs = with pkgs; [
    mopidy-bandcamp
    mopidy-mpd
    mopidy-youtube
  ];
in {
  <<pipewire>>
  <<firewallPorts>>
  services.mopidy = {
    enable = true;
    extensionPackages = extPkgs;
    configuration = ''
    [core]
    data_dir = /var/lib/mopidy

    [audio]
    # (ref:output)
    output = pulsesink server=127.0.0.1

    [bandcamp]
    discover_tags = 8-bit, chiptune, idm, ambient, drone, experimental, dark ambient, glitch, cyberpunk,  nu-jazz, gameboy, breakcore, edm, indie, indie-folk, indie-punk, indie rock, eugene, seattle, phoenix
    identity = ${builtins.readFile /home/rrix/org/cce/bandcamp-cookie.txt}

    [mpd]
    enabled = true
    port = 6601
    hostname = ::

    [youtube]
    musicapi_cookie = ${builtins.readFile /home/rrix/org/cce/ytmusic-cookie.txt}
    musicapi_enabled = true
    '';
  };
}

Firewall ports for MPD and Mopidy

networking.firewall.allowedTCPPorts = [ 6600 6601 ];

PipeWire-Pulse TCP audio

This configures PipeWire to listen for TCP connections so that the service-user which is set up for Mopidy is able to send audio to my speakers through a PipeWire run through My NixOS user.

(output) above points Mopidy at it.

# Set up PipeWire to listen on TCP so that the mopidy user can send audio
services.pipewire.config.pipewire-pulse = {
  "context.exec" = [
    {
      "args" = "load-module module-native-protocol-tcp";
      "path" = "pactl";
    }
  ];
  "pulse.properties" = {
    "server.address" = [
      "unix:native"
      "tcp:4713"
    ];
  };
};

Mopidy Bandcamp

Here's a simple little PyPI package wrapper. This lets me play from my wishlist and discovery tags and my collection. I add stuff on my phone when I'm bored and decide whether I want to buy them later on.

{  python3Packages, mopidy, yt-dlp, lib, ... }:

python3Packages.buildPythonApplication rec {
  pname = "mopidy-bandcamp";
  version = "1.1.5";

  src = python3Packages.fetchPypi {
    inherit version;
    pname = "Mopidy-Bandcamp";
    sha256 = "sha256-wg9zcOKfZQRhpyA1Cu5wvdwKpmrlcr2m9mrqBHgUXAQ=";
  };

  propagatedBuildInputs = [
    mopidy
  ];

  pythonImportsCheck = [ "mopidy_bandcamp" ];

  # has no tests
  doCheck = false;

  meta = with lib; {
    description = "Mopidy extension for playing music from Bandcamp";
    homepage = "https://github.com/impliedchaos/mopidy-bandcamp";
    license = licenses.mit;
    maintainers = [
      {
        email = "nixpkgs@whatthefuck.computer";
        matrix = "@rrix:kickass.systems";
        github = "rrix";
        githubId = 138102;
        name = "rrix";
      }
    ];
  };
}