Rune Audio All In One (Player and Ripper)
06/12/17


RuneAudio homepage
This is a project using a Raspberry Pi 3 that plays music (FLAC in this case) and will also automatically rip and add music if placed in a optical drive


Much of my inspiration came from the "Some Wide Open Space" wordpress site. But there were some things that didn't work and additional features I wanted.
Start ripping when CD is inserted

Here are the highlights of this project


Getting Started With Premade Rune Audio Image


Zip of image file (Needs a 16GB or larger SD card). 1.2GB zipped size.

Additional Steps Needed (until new image re-uploaded)
Burn this to an SD card and insert in a Raspberry PI 3 and it should be up and running.
USB music storage drive needs to be labeled "Local" and there needs to be a folder called "Music"
The following path should be valid: /mnt/MPD/USB/Local/Music/
Practise burning first by inserting a disc in the optical drive. Then type "abcde" from an SSH session console. If it works correctly, then uncomment the auto CD rip script in the crontab
[root@runeaudio boot]# crontab -e
#* * * * * /usr/local/sbin/audio-cd-rip.pl         ----------- Uncomment this line
30 * * * * /usr/local/sbin/kill_long_process.pl      
    
Then stick a disc in and within 60 seconds it should start ripping behind the scenes


Getting Started With RuneAudio Baseline Image

But to start off fresh, grab the basic image from RuneAudio website.
At the time of this write up the image created a 2GB file system and is already at 91% full upon first boot up.


===============  RuneOS distribution  ===============    
  ____                      _             _ _       
 |  _ \ _   _ _ __   ___   / \  _   _  __| (_) ___  
 | |_) | | | | '_ \ / _ \ / _ \| | | |/ _` | |/ _ \ 
 |  _  | |_| | | | |  __// ___ \ |_| | (_| | | (_) |
 |_| \_\\__,_|_| |_|\___/_/   \_\__,_|\__,_|_|\___/ 
        
================  www.runeaudio.com  ================
RuneOs: 0.3-beta
RuneUI: 1.3-beta
Hw-env: RaspberryPi 2


[root@runeaudio ~]# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       1.9G  1.6G  188M  90% /         ------ Problem, only 188MB free
devtmpfs        458M     0  458M   0% /dev
tmpfs           462M     0  462M   0% /dev/shm
tmpfs           462M   12M  450M   3% /run
tmpfs           462M     0  462M   0% /sys/fs/cgroup
tmpfs           462M  4.0K  462M   1% /tmp
logs            5.0M   36K  5.0M   1% /var/log
rune-logs       5.0M  120K  4.9M   3% /var/log/runeaudio
/dev/mmcblk0p1  100M   18M   83M  18% /boot
/dev/sda2       917G  2.9G  868G   1% /mnt/MPD/USB/Local
    
When you try to copy something over to the Rune system, if the file is bigger then the free space on the SD card, you will get an error. Even if you are trying to copy to the USB drive.

"There is not enough space on MusicStore. You need an additional XXX MB to copy these files"



So in order to transfer large files in the future, even if to an external USB drive, we need to enlarge the partition of the SD card.
[root@runeaudio ~]# fdisk /dev/mmcblk0

Welcome to fdisk (util-linux 2.27.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): p
Disk /dev/mmcblk0: 14.4 GiB, 15502147584 bytes, 30277632 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00005f47

Device         Boot  Start     End Sectors  Size Id Type
/dev/mmcblk0p1        2048  206847  204800  100M  c W95 FAT32 (LBA)
/dev/mmcblk0p2      206848 4401151 4194304    2G 83 Linux

Command (m for help): d
Partition number (1,2, default 2): 2

Partition 2 has been deleted.

Command (m for help): n
Partition type
   p   primary (1 primary, 0 extended, 3 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (2-4, default 2): 2
First sector (206848-30277631, default 206848): 
Last sector, +sectors or +size{K,M,G,T,P} (206848-30277631, default 30277631): 

Created a new partition 2 of type 'Linux' and of size 14.3 GiB.

Command (m for help): t
Partition number (1,2, default 2): 2
Partition type (type L to list all types): 83

Changed type of partition 'Linux' to 'Linux'.

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Re-reading the partition table failed.: Device or resource busy

The kernel still uses the old table. The new table will be used at the next reboot or after you run partprobe(8) or kpartx(8).

[root@runeaudio ~]# reboot

[root@runeaudio ~]# resize2fs /dev/mmcblk0p2
resize2fs 1.42.13 (17-May-2015)
Filesystem at /dev/mmcblk0p2 is mounted on /; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 1
The filesystem on /dev/mmcblk0p2 is now 3758848 (4k) blocks long.

[root@runeaudio ~]# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root        14G  1.6G   12G  12% /    ------ All better!

Package Updates Using Pacman


Now we need to install some updates. "pacman" is the "apt-get" equivalent on Arch Linux (the underpinnings of RuneAudio).
You may find you get errors while running pacman. This will probably be due to packages timing out while installing.....watch the download speed.


Here are the sequence of installs I used

pacman -Sy
pacman -S pacman systemd binutils patch gcc make
pacman -S libxml-perl libproxy perl-xml-parser perl-html-parser
pacman -S abcde cdparanoia eject flac libdiscid imagemagick glyr


If you get this message (4/4) Arming ConditionNeedsUpdate..., it's ok if it takes a while. It will with the first couple pacman updates.
:: Proceed with installation? [Y/n] y
:: Retrieving packages...
 linux-api-headers-4.10.1-1-armv7h     849.7 KiB   817K/s 00:01 [##################################] 100%
error: failed retrieving file 'glibc-2.25-1-armv7h.pkg.tar.xz' from mirror.archlinuxarm.org : Operation too slow. Less than 1 bytes/sec transferred the last 10 seconds
warning: failed to retrieve some files

error: failed to commit transaction (unexpected error)
Errors occurred, no packages were upgraded.
In the above, you can see "Operation too slow. Less than 1 bytes/sec transferred the last 10 seconds warning: failed to retrieve some files. Pacman will fail if it gets too slow. Just keep repeating the commands until they eventually work.


UDEV rule when audio Cd inserted

Add the following udev rule to let the system know an audio CD has been inserted.
Whenever an audio CD is inserted in the optical drive, a file called CD_IN will be created in /tmp/. This will be used to trigger a script that will start ripping the CD

[root@runeaudio ~]# ls -l /etc/udev/rules.d/99-cd-audio-processing.rules
ls: cannot access '/etc/udev/rules.d/99-cd-audio-processing.rules': No such file or directory
[root@runeaudio ~]# echo 'SUBSYSTEM=="block", KERNEL=="sr0", ACTION=="change", RUN+="/usr/bin/touch /tmp/CD_IN"' >> /etc/udev/rules.d/99-cd-audio-processing.rules
[root@runeaudio ~]# more /etc/udev/rules.d/99-cd-audio-processing.rules
SUBSYSTEM=="block", KERNEL=="sr0", ACTION=="change", RUN+="/usr/bin/touch /tmp/CD_IN"    
    

To make the udev rule active
[root@runeaudio ~]# udevadm control --reload    
    
Crontab to initiate CD ripping


Enable the crontab for implementing the auto-rip
[root@runeaudio ~]# systemctl enable cronie
Created symlink /etc/systemd/system/multi-user.target.wants/cronie.service → /usr/lib/systemd/system/cronie.service.
[root@runeaudio ~]# systemctl start cronie
[root@runeaudio ~]# crontab -e
    
Here are the two crons I have. You can add these at any time, just keep the 1st line commented out until everything is tested and running
#* * * * * /usr/local/sbin/audio-cd-rip.pl
30 * * * * /usr/local/sbin/kill_long_process.pl
    
audio-cd-rip.pl: Looks for /tmp/CD_IN every minute to determine if an audio CD is inserted in optical drive and if so attemps to rip the CD.
First the script deletes /tmp/CD_IN.
The script then checks if abcde is already running
If not, then abcde is started
Once abcde is completed, the CD is ejected. This is slightly different then having abcde eject the CD since abcde ejects the CD once the disc is done physically being read, but before encoding from WAV to FLAC has finished. Using eject in this script, the CD is not ejected until the entire abcde process is complete.

kill_long_process.pl: This script runs every 30 minutes with the cron and looks to see if there is an abcde process that has been running for more then an hour. If it finds one, it kills it. I had a scratched CD that abcde would not finish on....even after running over night, it never completed the track.

Copy both scripts to /usr/local/sbin

Make Changes To ABCDE


It wasn't made obvious to me, but abcde is actually just a REALLY well made shell script. As a result, we can edit it to serve our needs very easily.

check_cddb_choice.pl (put in /usr/local/sbin) is a script that looks at the CDDB choices and does a rough check to make sure that the chosen file by abcde is appropriate.
Since I couldn't get MusicBrainz to work, I ended up using CDDB and I found it had some issues with Japanese albums and some others.

The way CDDB works is that it looks at the number of tracks in a CD as well as the length in frames of each song on the CD and gets a unique "key" for that CD.
It turns out this is a very reliable way of associating track info to a CD, and explains why a CD copied with something like EAC will correctly look up when inserted into something that uses CDDB/Gracenote.
CD Text is different from CDDB lookup and is data actually stored on an Audio CD

This script will intercept the CDDB query information and make sure the 1st choice has appropriate characters. If it doesn't, then it will scan the other returns and if they pass then the script will move one of those other returns to the #1 position. Abcde always picks the #1 return.

    ### Around line 2909 ###
							do_cddbparse "$ABCDETEMPDIR/cddbread.$CHOICE"
							;;
						esac
					fi
				fi
			fi
		fi
	else
	echo ">>>>>>>>>>>>> Checking CDDB Query Returns <<<<<<<<<<<<<<<<<<"      ------- Add this line
	/usr/local/sbin/check_cddb_choice.pl                                     ------- Add this line
	echo ">>>>>>>>>>>>>>>>> Finished CDDB Check <<<<<<<<<<<<<<<<<<<<<<"      ------- Add this line
	sleep 3	                                                                 ------- Add this line
		# We're noninteractive - pick the first choice.
		# But in case we run a previous instance and selected a choice, use it.
		if [ -r "$ABCDETEMPDIR/cddbchoices" ]; then
			# Show the choice if we are not using the locally stored one
    





Enable pHAT DAC with RuneAudio
pHAT DAC uses the same hardware (Burr Brown/Texas Instruments PCM5102A) as the HiFi Berry DAC. So we can enable the HiFi Berry settings and pHAT Dac will work

[root@runeaudio ~]# cd /boot
[root@runeaudio boot]# vi config.txt 
    
---------------------------------------------------------------------------    
    
#dtparam=spi=on
#dtparam=act_led_trigger=mmc

#dtparam=uart1=off
#dtoverlay=pi3-disable-bt-overlay

# Uncomment one of these lines to enable an audio interface
dtoverlay=hifiberry-dac        ------ Uncomment this line and reboot Raspberry Pi
#dtoverlay=hifiberry-dacplus
#dtoverlay=hifiberry-digi
#dtoverlay=hifiberry-amp
#dtoverlay=iqaudio-dac

---------------------------------------------------------------------------

[root@runeaudio boot]# reboot

    
Then once the system is back up, open runeaudio.local and enable the HiFi Berry DAC







Clean Up Library Page

Since this will be a purpose built local music player, I removed all the library sources I did not need. I just commented it out, so the code is still present in case I ever need something



The code to change is in /var/www/assets/js/runeui.js. Comment out the sections about Webradio, Spotify, Dirble, Jamendo. And I did network as well.
    // if (chkKey(obj.webradio)) {
    // // webradios block
        // if (obj.webradio === 0) {
            // if (notMPD) {
                // content += divOpen + '

My Webradios (0)

webradio local playlists
' + divClose; // } else { // content += divOpen + '

My Webradios (0)

click to add some
' + divClose; // } // } else { // content += divOpen + '

My Webradios (' + obj.webradio + ')

webradio local playlists
' + divClose; // } // } // if (chkKey(obj.Spotify)) { // // Spotify block // if (obj.Spotify === '0') { // content += divOpen + '

Spotify

click to configure
' + divClose; // } else { // if (obj.ActivePlayer !== 'Spotify') { // content += divOpen + '

Spotify

click to switch renderer
' + divClose; // } else { // content += divOpen + '

Spotify

music for everyone
' + divClose; // } // } // } // if (chkKey(obj.Dirble)) { // // Dirble block // content += divOpen + '

Dirble (' + obj.Dirble + ')

radio stations open directory
' + divClose; // } // Jamendo (static) // content += divOpen + '

Jamendo

world\'s largest platform for free music
' + divClose; // Album list (static)
You can also change "USB Storage" for the local to whatever you want with these lines....I chose Song Storage
     if (chkKey(obj.USBMounts)) {
    // USB mounts block
        if (obj.USBMounts === 0) {
            if (notMPD) {
                content += divOpen + '

USB storage (0)

no USB storage plugged
' + divClose; } else { content += divOpen + '

Song Storage (0)

no USB storage plugged
' + divClose; } } else { content += divOpen + '

Song Storage (' + obj.USBMounts + ')

USB attached drives
' + divClose; } }

Add Basic Artwork And File Maintanence Page To Interface

Abcde downloads an image ("cover.jpg") and places it in the directory of the ripped album. Rune then uses that picture as the cover art to display while playing. Rune can also display embedded artwork in each file, but this image works just as well.
The problem is that CDDB may select a wrong image or a low quality image. MusicBrainz may be better, but I couldn't get that to work.
So this page allows me to upload a new cover art image either from a URL or a file from the device. It also allows me to change the album title of each folder (and album tags of each file in folder.

Important Note: To make changes to RuneAudio PHP take effect, you must type this everytime you make a change (rebooting also works). RuneAudio runs off of NGINX web server.
If you need to do this many times, just hit the UP arrow key to display the previous command

[root@runeaudio ~]# systemctl restart php-fpm.service    
    

This is the first thing I have written in PHP, so all I can say is that it works ^_^
I would have been more comfortable using Perl, but NGINX does not natively support Perl CGI and I didn't have luck integrating FastCGI Wrap.



This will load the file structure of /mnt/MPD/USB/Local/Music. You can change the name of each album title (folder name and embedded tag name). You can also upload new album art either from web URL or from your device.



The album name option will change the name of the album folder (and sanitize characters for safe folder names), and it will use metaflac to write new album tag to each flac file in the folder (will not sanitize that though).


Cover art option uses either a URL or a local image to copy to /mnt/MPD/USB/Local/Music/Artist/Album/cover.jpg. Image is converted to JPG and scaled to 600px wide.



HDMI Display For Enclosure

I added a 5" 800x480 HDMI display. I used one that specifically had HDMI and power on the side to make mounting in a enclosure easier.
The display is generic Ebay unit from China. I did not see any identifier on it. It says it can use touch through the USB cord, but I haven't tried it yet (USB cable is plugged into dedicated charger for now)
I had to edit /boot/config.txt to force the resolution.
# Uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
hdmi_drive=2
hdmi_mode=87
hdmi_cvt=800 480 60 6 0 0 0
I then had to make the local displayed browser different that what is seen on the "remote" devices. I made this more compact and visible.
This works by adding in some PHP logic to see if the browsers IP address is 127.0.0.1. If so, it sends modified HTML. I also created a new CSS file (/var/www/assets/css/runeui_local.css) since the scaling of various elements needed to be adjusted. The CSS file gets loaded in header.php, depending on the IP Address of the browser making the request.
I tweaked various widths and heights to get everything to use as much space as possible and be readable from a distance



SATA controller

For SATA bridge, I am using 2x Aleratec Raspberry Pi Add On USB to SATA Converter Board Adapter. I went this route because I had some issues getting power to the SATA devices and I wanted to use dedicated power sources. This board does not have power. Had no issues so far with either the HDD or DVD-Drive.


Files Updated or Added

Zip of all files updated
These files all work on the assumption that music is stored on a USB volume called "Local" and in a folder called "Music". /mnt/MPD/USB/Local/Music
/no_cover_art.jpg
/boot/config.txt
/usr/local/sbin/audio-cd-rip.pl
/usr/local/sbin/check_cddb_choice.pl
/usr/local/sbin/kill_long_process.pl
/var/www/coverart.php
/var/www/album.png
/var/www/artist.png
/var/www/upload.php
/var/www/apps/template/playback.php
/var/www/apps/template/header.php
/var/www/assets/css/runeui.css
/var/www/assets/css/runeui_local.css
/var/www/assets/js/runeui.js