Raspberry Pi As Music Jukebox Using MPD

12 February 2014

See also: Hi-Fi With Your Pi

Preface

The most annoying part of using a Raspberry-Pi as MPD jukebox is that if it occasionally gets powered off, there will inevitably be a crash upon start one day.

Every few restarts trigger an fsck file systems check, which discovers a mismatch between current time and timestamp of when last mounted because without continuous power, the clock will revert to a long past date. This leads to the system going into single user mode, awaiting manual intervention.

That part becomes a showstopper for an unattended device that is supposed to “just work” or otherwise is without keyboard/monitor attached.

The fix for you might simply be to always keep the 5V power flowing to the device, and if powering down the rest of the gear, ensuring that just the R-Pi remains on. The net effect is that this contributes far less EMF than the alarm clock next to most people’s bed.

Introduction

Ideally, when you want to hear music everything should just work, but what it means to “just work” is different for each person.

I want high quality sound with reasonably straight-forward setup and willing to spend more for components beyond merely docking a portable music player or mobile phone.

For me, this means individual components for storing songs versus processing digital audio and separate speakers with fairly wide range.

Following a bit of background on current options and decisions made, gritty details about software configuration and off-the-shelf components are given. Most may be followed by someone with cursory knowledge of Linux command-line access.

Background

Raspberry Pi runs mpd, and if your music collection fits within remaining space of a 32GB SD Card, your music collection may reside there too. Here, music is pulled from another machine on local network via sshfs.

Overview

There was a product review in Mix Magazine that suggested an affordable setup for small rooms and/or tiny apartments such as studios common to lower Manhattan.

I’ll omit product details from their article, as it’s been long enough to be irrelevant now. Let’s just say that these were of solid build quality despite a consumer-oriented badge affixed to the outside. This happens every now and again.

Paraphrasing from memory, the article advised:

For a small room, 30W amplification is more than sufficient, as you’re still within the rule of thumb that you’re only using a small fraction of the equipment’s potential while having quality sound at comfortable levels for everyone in the room.

This will especially be appreciated by neighbors on other side of common walls and ceiling/floor!

Without the insider information of specific manufacturing details that they had at Mix, the new configuration below begins one step higher than that earlier stack to ensure consistent quality for those reading this after any one particular product’s lifetime.

All That Audio Jargon

The current state of home audio can confuse and bewilder an otherwise intelligent and resourceful person.

For instance, the term “pre-outs” which identifies the non-headphone output jacks of an amplifier, might be mistakenly interpreted as unamplified since prefix “pre-” is commonly understood to mean that which comes before something else. However, since the component that most of use would think of as amplifiers are technically called “pre-amps”, that earlier jargon is simply indicating outputs of a specific device.

All this jargon has its place, but tracking this down can be challenging and be more effort than most are expecting to apply.

We simply want good quality music– is that too much to ask?

This challenge is especially the case perhaps when someone had a decent audio system handed-off when a relative had upgraded to something new or perhaps when one seeks to step beyond the limited quality of common portable music players.

One other bit of jargon worth mentioning: volume knob is sometimes called the pot– as in potentiometer– which makes sense from an electrical engineering perspective.

Equipment

Desired setup:

  1. Dedicated music player– not portable, not mobile phone
  2. Outboard digital audio converter (DAC)
  3. Headphone class amplifier (“pre-amp”)
  4. Unobtrusive cafe class speakers
  5. Multiple options for controlling song/playlist selections

Room Description

Typical San Francisco apartment– this setup is for a room of 12'x18' with 9' ceiling and 10'x3' slanted bay window.

1920’s wood frame apartment building: plaster & lath walls, hardwood floors with 30% coverage area rug, cloth curtains (always opened/bunched=baffles), 18-20 sq ft total glass in bay window.

Musical Tastes

Mainly acoustic, world music (Celt, Turk, tango… Lots of accordion!), classical symphony, chamber, Acid Jazz, and a bit of new wave & alternative rock from 1980’s. Dance Industrial (admit it– that was fun!) and early electronica (i.e., “if you’re listening to lyrics, you’re not hearing the music” -XLR8R, newsprint era) have largely been shelved.

Story

After unloading all our audio/video gear upon moving cross-country, I finally ordered replacements: schiit (as noted on their website, yes, it’s pronounced exactly how you suspect) with details below.

The DAC is being driven from an underpowered Raspberry Pi with uncompressed .flac ripped from CDs (via abcde with cdparanoia). Fortunately, we’ve only ever bought one album as digital download, so I’ve re-rendered from CD via disc tray of far better quality than the high error-rate Macbook Pro slot-loader.

In all, my setup will resemble this: http://www.audiostream.com/content/raspberry-pi-half-baked-50-network-player, albeit set in motion before his posting. I’m sticking with Raspian/debian, as it’s already running, and playback sounds good from my R-Pi (i.e., no “jitter” or other audio artifacts, so CPU+RAM is sufficient).

User Interface will be QMPDclient on Linux, MpcOSX for MacOSX (Tiger and newer; source code available) in addition to MPDroid for Android. That’s the beauty of MPD: each client may be independent, and it only needs to run to trigger changes or to view state. So then, you can launch a playlist from one and query from another.

(Full HD touch screen panels of various sizes are available now but weren’t when this was originally done.)

All of my current music rotation fit uncompressed within 16GB, which a 32GB maximum SDcard of a Raspberry-Pi would easily accommodate. Having our entire library of discs ripped (New Order, full catalog of David Bowie, etc), that’s over 70 GB, so the Pi is backed by a 1TB disk in a closet server, which is also used to rip & encode. Playback while streaming from there also had no obvious jitter or other artifacts.

I made some miscalculations, but in the end, it all worked.

I had mistaken the power consumption wattage for power output when originally reading specs the amp. So this isn’t a 30W per channel amp (or rather, a “pre-amp” technically), it’s only 1W per channel but consumes 30W and gets warm… The 30W criteria was based upon my old system purchased based entirely upon recommendations from a Mix Magazine article long ago and was powerful enough to be heard throughout our old house– let alone small apartments.

The current one (Asgard 2 from Schiit.com) is sufficient for comfortable volume within the same room, and that was the intended use! However, their 6 watt Lyr or 8 watt Mjolnir models would have been a better choice here.

Again, the rule of thumb is to only use about one tenth of your amp’s power so that you get cleaner sound. With this Asgard, however, I’m at 50-60%. But for tracks from Azam Ali, Redwood Tango Ensamble, Rodrigo y Gabriela, ZoĆ« Keating and various world music tracks– it’s very clean sound.

Hindsight / Lessons Learned

One major usability problem was due to the R-Pi omitting a persistent clock.

The issue is that upon cold boot such as after a power outage, the filesystem timestamp will be in the future compared to the system clock default value; therefore, Linux boots into single-user mode when fsck inevitably fails. And that’s unexpected behavior for anyone else using the music system.

Apparently, there are clock+battery components that plug into a daughterboard for the Raspberry-Pi which would be one way to solve the problem.

Another potential problem: with an outboard DAC and separate amplifier is that ReplayGain must be applied in software before ever reaching the DAC.

Gear

Music files are prepared on one machine with DVD tray (slot-loading disc players are apparently problematic), and playing occurs on another.

Preparing:

  1. Uncompressed .FLAC files ripped from CDs via abcde (calls cdparanoia internally)
  2. Normalize volume of audio tracks for ReplayGain, using metaflac
  3. Convert to fixed bitrate MP3 for compatibility with additional devices, using LAME or libav-tools

Playing:

  1. Rasberry Pi running mpd+sshfs+autofs = superjukebox!
  2. Using maximum size SD Card that R-Pi model B will accept: 32GiB.
  3. Outboard Digital-to-Analog Converter with USB connection to Raspberry Pi
  4. Headphone class amplifier with dual RCA outputs (rather than XLR ports)
  5. Speaker Cables: generic IEC 18 AWG 6' Speaker Wire with RCA Male
  6. Unobtrusive “cafe” class speakers
  7. User interface:
  8. Touch Screen LCD monitor compatible with Linux; e.g., http://www.newegg.com/Product/Product.aspx?Item=9SIA0V129P5310 (Ubuntu before 15.10 might need a backported kernel)

Because this amp is actually of a headphone class, be prepared to ajust volume beyond 50% to just hear it in same room when using passive speakers with 1 Watt per channel amp like Asgard, but at least this setup is fundamentally incapable of bothering neighbors!

Note absence of subwoofer or much low end range– for same reasons to not disturb neighbors in apartment building. When we want to feel the music, we go to a performance instead.

Also, since distance between amp and speakers is only a few feet– as opposed to hundreds of feet in a nighclub or auditorium– skip the fancy braided speaker cables.

But if you’re so inclined:

Raspian/debian config

Use Raspian tool for minimal configuration changes:

Run: sudo raspi-config

Early versions failed while setting Locale; stuck on en_GB.UTF-8. Force other Locale:

export LANGUAGE=en_US.UTF-8
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
sudo locale-gen en_US.UTF-8
sudo dpkg-reconfigure locales

Get Updates

sudo apt-get update
sudo apt-get dist-upgrade

Add Packages

sudo apt-get install rsync mpd mpc sshfs autofs ntpdate screen

Auto-play

sudo tee /etc/rc2.d/S04mpc <<END.
#! /bin/sh
su - pi -c "mpc play"
END.

Fix file permissions:

sudo chmod 0755 /etc/rc2.d/S04mpc

Network Interfaces

It may be useful having a static IP address for your music player, so you always know where to find it.

Edit /etc/network/interfaces, comment-out the “dhcp” setting for primary ethernet (eth0), and give it a static address:

##iface eth0 inet dhcp
iface eth0 inet static
    address 192.168.1.31
    netmask 255.255.0.0
    gateway 192.168.42.1
    dns-nameservers 192.168.42.1

If logged onto R-Pi remotely, be sure to apply net config changes within subshell in background so you don’t lock yourself out of the system:

(sudo ifdown eth0 ; sudo ifup eth0) &

Network Time Protocol

Add local ISP time server to /etc/ntp.conf:

server time.sonic.net

SSH Daemon Config

Optional but highly recommended, append to /etc/ssh/sshd_config file:

sudo tee -a /etc/ssh/sshd_config <<END.
ServerKeyBits 4096
PasswordAuthentication no
ChallengeResponseAuthentication no
PermitRootLogin no
UsePAM no
IgnoreUserKnownHosts yes
END.

No known key, no access. No login prompt means no brute-force password attacks.

Apply changes:

service ssh restart

File Server

Do the following on a beefier machine than Raspberry Pi, ideally something with a tray-loading disc player (rather than slot-loading types such as those on Apple laptops).

Here, we’re using Ubuntu Server 12.04 LTS (a.k.a. “precise”) on Intel x86-64 with conventional devices.

CDDA disc ripping

From file server with CD or DVD tray, run:

sudo apt-get install abcde cd-discid flac cdparanoia libav-tools lame

~/.abcde.conf

Contents of ~/.abcde.conf on server with CD or DVD tray: (Highlights, from Andrew’s Corner.)

FLACENCODERSYNTAX=flac
FLAC=flac
FLACOPTS='--verify -0'
OUTPUTTYPE="flac"

CDROMREADERSYNTAX=cdparanoia
CDPARANOIA=cdparanoia
CDPARANOIAOPTS="--never-skip=4"

CDDISCID=cd-discid
##Default CDDB server is FreeDB http://www.freedb.org/
###CDDISCIDOPTS=--musicbrainz
###CDDBMETHOD=musicbrainz

OUTPUTDIR="$HOME/music/"
ACTIONS=cddb,playlist,read,encode,tag,move,clean

OUTPUTFORMAT='${OUTPUT}/${ARTISTFILE}-${ALBUMFILE}/${TRACKNUM}.${TRACKFILE}'
VAOUTPUTFORMAT='${OUTPUT}/Various-${ALBUMFILE}/${TRACKNUM}.${ARTISTFILE}-${TRACKFILE}'

ONETRACKOUTPUTFORMAT='${OUTPUT}/${ARTISTFILE}-${ALBUMFILE}/${ALBUMFILE}'
VAONETRACKOUTPUTFORMAT='${OUTPUT}/Various-${ALBUMFILE}/${ALBUMFILE}'

PLAYLISTFORMAT='${OUTPUT}/${ARTISTFILE}-${ALBUMFILE}/${ALBUMFILE}.m3u'
VAPLAYLISTFORMAT='${OUTPUT}/Various-${ALBUMFILE}/${ALBUMFILE}.m3u'

# Put spaces in filenames instead of the more correct underscores:
mungefilename ()
{
  echo "$@" | sed s,:,-,g | tr / _ | tr -d \'\"\?\[:cntrl:\]  # "'
}

MAXPROCS=2      # Run a few encoders simultaneously
PADTRACKS=y     # Makes tracks 01 02 not 1 2
EXTRAVERBOSE=y  # Useful for debugging
EJECTCD=y       # Please eject cd when finished :-)

With each CD

The device below, sr0, may be different on your server. Again, this is for Ubuntu 12.04.

For each music CD, run:

abcde -d /dev/sr0

Merge Playlists

Once all discs have been converted to .flac files, create a unified playlist called everything.m3u.

From file server, run: (see below for Go code)

sudo apt-get install golang
cd /home/pi/music/
go run ~/bin/merge-playlists.go flac > everything.m3u

Optionally, edit everything.m3u file using favorite text editor (Emacs, of course) to prune as one way to create additional playlists. Otherwise, use an MPD client to create additional playlists.

Playlist File Formats

Strictly speaking, the .m3u8 file extension should be used since these are UTF-8 files. Proper .m3u files should use Latin-1 encoding. See http://en.wikipedia.org/wiki/M3U#Format

To convert, use:

mv everything.m3u everything.m3u8 
iconv -f UTF8 -t LATIN1 everything.m3u8 > everything.m3u

(Alternatively, within Emacs use: M-x encode-coding-region iso-8859-1)

Then rename affected files based upon output of:

diff everything.m3u8 everything.m3u

But this should only be an issue when playing tracks from these playlists via USB storage device on a mainstream sound system such as Yamaha CRX-N560 mini-system released 15 January 2014. (Yes, even contemporary systems progressive enough to support FLAC still fail to adequately handle UTF8 file encoding.)

ReplayGain

Apply ReplayGain metadata to each audio track off-line (rather than Volume Normalization for each song on-demand). This must work on entire music collection in one command– albeit, using a two-pass process.

screen
metaflac --add-replay-gain --preserve-modtime */*.flac

Check timestamp of subdirectories to confirm all have been converted.

Here, we perserve file modification timestamps with --preserve-modtime flag.

One issue apparently is that ReplayGain might accomplish nothing when your outboard DAC is isolated from your amplifier. That is, this requires either an all-in-one unit or completely software based processing.

Frequency

This is different than Sample Rate (e.g., 44.1 kHz or 48 kHz).

Researchers of brain states and frequencies encourage re-encoding music libraries from 440Hz to 432Hz, meaning this is the frequency of the first A note. At 432, this permits notes such as Middle C to remain a whole number (256Hz or 128Hz), but 440 makes it a fraction (261.63Hz or 130.828Hz). The conventional explanation is that one Hertz is one cycle per second, and one second is 1/86,400th of Earth’s daily rotation. This in turn implies that harmonic frequencies might be at play here. Apparently, classical composers Bach, Chopin, Mozart, and the usual suspects may have used 432 (sometimes “the old French pitch”), and benefits of 432 may be found in Cognitive Neuropsychiatry, 2014 January 24.
More: http://www.viewzone.com/432hertz222.html

Try it for yourself, and to quote the Oracle, “I expect what I’ve always expected, for you to make up your own damn mind.”

But of course, first you need to know whether or not to change the pitch of a particular recording, and accurate pitch identification is nontrivial.

A tool to do inspect as well as alter pitch include Audacity on Linux, MacOSX, Windows: https://sourceforge.net/projects/audacity/ with tutorials at audacityflex.com. Debian & Ubuntu packages should already include libflac++6v5 or similar.

sudo apt-get install audacity

To detect pitch, add the “Pitch Detect” plug-in: http://wiki.audacityteam.org/wiki/Nyquist_Analyze_Plug-ins#Pitch_Detect

Then for UI workflow to change pitch, follow: http://www.roelhollander.eu/en/432-tuning/how-to-changing-the-concert-pitch/

Or via batch processing:

vi ~/.audacity-data/Chains/440-to-432-(FLAC).txt
ChangeSpeed: Percentage=-1.818000
ExportFlac:

Then:

See also:

Update .flac Metadata

Genre isn’t always what you might expect from the CD database, so fix it:

cd flac/album.../
metaflac --show-tag=GENRE *.flac
metaflac --preserve-modtime --remove-tag=GENRE --set-tag=GENRE=Instrumental *.flac

Raspberry Pi as Jukebox

Slight variations from “mpd+sshfs+autofs=superjukebox!” posting: http://www.raspberrypi.org/phpBB3/viewtopic.php?f=29&t=23156

But need to add Pi’s SSH private key (no password) to /root/.ssh for autofs: https://help.ubuntu.com/community/Autofs

Playing music from Pi: install & run a client http://mpd.wikia.com/wiki/Clients

Install software

sudo apt-get update
sudo apt-get install mpd mpc sshfs autofs 

Optionally, for using Raspberry-Pi’s desktop or X11 clients:

sudo apt-get install qmpdclient

Ensure local server is reachable

Ensure local server exists within /etc/hosts:

echo "192.168.1.32 cubit" | sudo tee -a /etc/hosts

Confirm server is reachable:

ping cubit

Configure RSA key authentication for Raspbian root user

sudo ssh-keygen -t rsa
sudo cp ~pi/.ssh/id_rsa /root/.ssh/

Capture server’s host key into /root/.ssh/known_hosts, but you only need to go as far as “Permanently added ‘cubit,192.168.1.32’ to the list of known hosts.”

sudo ssh cubit

Configure autofs to mount remote SSH filesystem on demand

Append the following line to /etc/auto.master:

# ...preserve existing!
/mnt /etc/auto.sshfs uid=1000,gid=1000,--timeout=30,--ghost

The above line will mount sshfs partitions inside /mnt directory with user permissions.

Add remote mounts to /etc/auto.sshfs, where cubit is name of file server: (Yes, that’s actually a colon at beginning of line and some embedded backslashes, not a typo from org-mode markup)

: cubit -fstype=fuse,ro,nodev,nonempty,noatime,allow_other,max_read=65536 :sshfs\#pi@cubit\:music

Restart service:

sudo service autofs restart

Confirm: (again, cubit is name of local file server)

ls /mnt/cubit

Preliminary To Configuring MPD

With USB DAC connected, confirm your OS sees it. Run:

aplay -l

Ignore the section pertaining to “card 0: ALSA …”

Look for something like “card 1: Device [Schiit USB Audio Device], device 0: USB Audio [USB Audio]”

Confirm whether or not DAC supports mixer, etc. Run:

amixer scontrols

Returning “Simple mixer control ‘PCM’,0” indicates no hardware mixer on outboard DAC.

Configure MPD

mkdir -p ~/mpd/playlists

Running mpd as mpd user, here’s /etc/mpd.conf:
(cubit is name of file server)

filesystem_charset      "UTF-8"
id3v1_encoding          "UTF-8"

#music_directory    "/mnt/cubit"      # for file server
music_directory     "/home/pi/music"  # for SD card

playlist_directory  "/home/pi/mpd/playlists"
db_file         "/home/pi/mpd/tag_cache"
log_file        "/run/mpd/mpd.log"
pid_file        "/run/mpd/pid"
state_file      "/home/pi/mpd/state"
sticker_file        "/home/pi/mpd/sticker.sql"
user            "mpd"
bind_to_address         "any"
metadata_to_use         "artist,album,title,track,name,date,composer,performer,disc"
auto_update     "no"
zeroconf_enabled    "no"

replaygain      "auto" # Use "auto" for shuffle-play
#volume_normalization   "yes"  # remove when ReplayGain has been applied to audio tracks

# # For built-in audio-out jack:
# audio_output {
#    type           "alsa"
#    name           "bcm2835 ALSA"
#    device         "hw:0,0"
#    format         "44101:16:2"     # remove for outboard DAC
#    mixer_type         "software"      # remove for outboard DAC
#    mixer_device       "default"
#    mixer_control      "PCM"
#    mixer_index        "0"
#    replay_gain_handler    "software"
# }

# For outboard DAC: Schiit BIFROST
audio_output { 
        type            "alsa" 
        name            "Device USB Audio" 
        device          "hw:1,0" 
#        mixer_device       "hw:1" 
#        mixer_control      ""
    replay_gain_handler "software"
} 

Ensure no duplicate entries in mpd.conf, as conflicts result in an error which in turn means no audio.

Apply ReplayGain to each audio track off-line (rather than Volume Normalization for each song on-demand).

Start MPD & Update Your Database

sudo service mpd restart
mpc update

Once various discs have been ripped and encoded

From Raspberry Pi, run:

mpc load world.m3u
mpc random on
mpc play

For Linux console or X11 session, use: qmpdclient

For Android: MPDroid

Runbook: Adding Individual Albums

Once you’ve converted entire music collection, incrementally add new music.

Again, in commands below cubit is name of file server that contains tray-loading CD/DVD drive and very large disk. All commands run from there unless indicated by ssh pi@pi, which of course would be the Raspberry-Pi.

Scan Disc

For each music CD, run:

abcde -d /dev/sr0

(Be sure to also run mpc update to get the new tracks registered.)

Confirm Album Name

Quickly fetch actual name of newly added albums:

ls -lt ~/music/flac/ | head

Incrementally Update Playlists

For adding just one album’s generated playlist into another, append:

cd ~/music/
sed 's%^%flac/Cuarteto-De Querusa/%' < "flac/Cuarteto-De Querusa/De Querusa.m3u" >> everything.m3u

Occasional – ReplayGain

Re-run ReplayGain across entire collection:

screen
metaflac --add-replay-gain --preserve-modtime */*.flac

Optional – FLAC to MP3

Convert FLAC to fixed-bitrate MP3 for one album only:

cd ~/music/generated-mp3/
(cd ~/music/flac/ && find Cuarteto-De\ Querusa -name '*.flac' -print0) | xargs -0 ~/bin/flac-to-mp3.sh ~/music/flac

Optional – Local Storage

Copy to local storage on Raspberry-Pi:

ssh pi@pi
rsync -av pi@cubit:music/generated-mp3/ ~/music/mp3/
rsync -av pi@cubit:music/*.m3u ~/music/

Add -n flag to rsync as no-op (dry run) to test first, if you’d like.

When Converting To MP3

Translate playlists for MP3:

ssh pi@pi
cd ~/music/
sed -i 's/\.flac/.mp3/' *.m3u
sed -i 's/^flac/mp3/' *.m3u

Update MPD database

Required, as our mpd.conf states to not auto-update.

ssh pi@pi
./update.sh

Appendix: Scripts & Source Code

flac-to-mp3.sh

Source code:

#! /bin/bash

# Usage:
# For entire collection, use with: find | xargs
#   cd  ~/music/generated-mp3/
#   (cd ~/music/flac/ && find . -name '*.flac' -print0) | \
#       xargs -0 ~/bin/flac-to-mp3.sh ~/music/generated-mp3
# For one album only, replace first argument of 'find' with subdirectory of album;
# e.g., replace the single dot with something like "My Diva-EP".

SRC="$1"
shift

for x in $*; do
  a="$1"
  DIR=$(dirname "$a")
  [ -d "$DIR" ] || mkdir "$DIR"

  # give output correct extension
  ##OUTF=$(basename "$a" .flac).mp3
  OUTF="${a[@]/%flac/mp3}"

  echo "$SRC/$a => $OUTF"

  # get the tags
  ARTIST=$(metaflac "$SRC/$a" --show-tag=ARTIST | sed s/.*=//g)
  TITLE=$(metaflac "$SRC/$a" --show-tag=TITLE | sed s/.*=//g)
  ALBUM=$(metaflac "$SRC/$a" --show-tag=ALBUM | sed s/.*=//g)
  GENRE=$(metaflac "$SRC/$a" --show-tag=GENRE | sed s/.*=//g)
  TRACKNUMBER=$(metaflac "$SRC/$a" --show-tag=TRACKNUMBER | sed s/.*=//g)
  DATE=$(metaflac "$SRC/$a" --show-tag=DATE | sed s/.*=//g)

  # stream flac into the lame encoder
  flac -c -d --apply-replaygain-which-is-not-lossless=t "$SRC/$a" | \
    lame -V0 --add-id3v2 --pad-id3v2 --ignore-tag-errors \
     --replaygain-accurate \
     --ta "$ARTIST" --tt "$TITLE" --tl "$ALBUM"  --tg "${GENRE:-12}" \
     --tn "${TRACKNUMBER:-0}" --ty "$DATE" - "$OUTF"

  shift || break
done

Usage:

cd  /home/pi/music/generated-mp3/
(cd /home/pi/music/flac/ && find . -name '*.flac' -print0) | \
    xargs -0 ~/bin/flac-to-mp3.sh /home/pi/music/generated-mp3

And convert playlists: (originals get .m3u_FLAC file extension)

sed -i_FLAC 's/\.flac/.mp3/' *.m3u && sed -i 's/^flac/mp3/' *.m3u

merge-playlists.go

Source code:

// merge-playlists.go - Merge .m3u playlists and insert absolute paths

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "sort"
    "strings"
)

func walk(dir string) (playlist []string) {
    path := dir
    prefix := fmt.Sprintf("%s/", dir)
    if dir == "" {
        path = "."
        prefix = ""
    }
    fileInfo, err := ioutil.ReadDir(path)
    if err != nil {
        fmt.Println("Error:", err)
    }

    for _, f := range fileInfo {
        name := f.Name()
        path = fmt.Sprintf("%s%s", prefix, f.Name())
        if f.IsDir() {
            if name[0] == '.' {
                continue
            }
            defer func(path string) {
                playlist = append(playlist, walk(path)...)
            }(path)
        } else if strings.HasSuffix(name, ".flac") {
            playlist = append(playlist, path)
        } else if strings.HasSuffix(name, ".mp3") {
            playlist = append(playlist, path)
        } else if strings.HasSuffix(name, ".m3u") {
            continue
        } else {
            fmt.Fprintf(os.Stderr, "Ignoring: %s\n", name)
        }
    }
    return
}

func main() {
    dir := ""
    if len(os.Args) > 1 {
        dir = os.Args[1]
    }
    // Coerce types from []string to sort.StringSlice, of which
    // the latter is essentially an alias for the former:
    playlist := (sort.StringSlice)(walk(dir))
    playlist.Sort()
    fmt.Println(strings.Join(playlist, "\n"))
}

Usage:

sudo apt-get install golang
cd music/
go run merge-playlists.go flac/ > everything.m3u

reset.sh

Wipe all state from mpd, and start fresh.

Source code:

#! /bin/sh
sudo service mpd stop 
rm -rf ~/mpd/{state,sticker.sql,tag_cache}
[ -d ~/mpd/playlists ] || mkdir ~/mpd/playlists
chgrp -R audio ~/mpd
chmod -R g+w ~/mpd
sudo service mpd start 
mpc --wait update
playlist="${*:-everything.m3u}"
mpc --wait load "$playlist"
mpc repeat on 
mpc random on 
mpc --wait play 

Using scripts and configuration exactly as presented in this document requires running the reset.sh script after loading new albums.

This is also helpful for reshuffling random play when mpd starts to “favor” certain albums.

update.sh

Load playlist but without interrupting current song.

Source code:

#! /bin/sh
mpc crop
playlist="${*:-everything.m3u}"
mpc --wait load "$playlist"
mpc --wait play
Copyright © 2014, 2015 Daniel Joseph Pezely
May be licensed via Creative Commons Attribution.