Music Player Deamon and Jukebox

A raspberry Pi-4 to replace my CD player and library


               documentation

DAC user manual.pdf

introduction

I've installed the apache-2 webserver on my RPi-4 (sudo apt-get install apache-2 -y). This is not a secure webserver. However, installing a secure webserver is not really an option given the price of a certificate: about 100€ annually. Because my other raspberry also has a webserver that uses the standard port 80, the RPi had to be configured to use port 443 and the network firewall instructed to direct port 443 to the RPi-4. Several .conf files had to be edited:

/etc/apache2/ports.conf"Listen 80"changed intoListen 443"
/etc/apache2/sites-enabled/000-default.conf"VirtualHost *:80"changed into"VirtualHost *:443"
/etc/apache2/sites-enabled/your RPi's name.conf"VirtualHost *:80"changed into"VirtualHost *:443"

The RPi comes with a secure sFTP server installed for file transfer.
I communicate with the RPi-4 using the terminal emulator PuTTY installed on my PC. For a detailed description and how to activate SSH on your RPi see this page.

 
Adding the SSHD hard disk
to top of page    Published: 1/2/2021

Installing a disk on Raspberry Pi OS is not plug&play but requires several steps to go through:
1 I plugged a Samsung 0.5 Tb extern SSD drive into one of the RPi's USB I/O ports.
2 Made a new folder in the /mnt folder (I called this folder T5): "sudo mkdir /mnt/T5
3 Mounted the device: "sudo mount /dev/sda1 /mnt/T5"
4 You need to know the PARTUUID of the device: "blkid | grep sda1
   This returned in my case: /dev/sda1: LABEL="Samsung_T5" UUID="D057-2D76" TYPE="ext4" PARTUUID="237f827a-01"
5 Then using the nano editor "sudo nano /etc/fstab" add the line (with your PARTUUID and not the one above of course):
   PARTUUID=237f827a-01 /mnt/T5 ext4 defaults,auto,users,rw,nofail,x-systemd.device-timeout=30,umask=000 0 0
6 You can verify if the drive is mounted by "df -h"
7 In case of problems try: "sudo dsck.exfat /dev/sda1"
8 I then created a folder "music" on the disk and put my library of mp3 files in this folder using FTP.


 
Installing the Music Player Daemon
to top of page    Published: 1/2/2021

The Music Player Daemon (MPD) can be obtained from musicpd.org by typing "sudo apt install mpd" on the terminal. It also has a very complete manual.
In the "/etc/mpd.conf" file a few lines need to be either edited or uncommented by removing the hashtags:
music_directory                "/mnt/T5/music" (my case, the default is "/var/lib/mpd/music")
playlist_directory              "/mnt/T5/playlists" (my case, the default is "/var/lib/mpd/playlists")
db_file                              "/mnt/T5/tag_cache" (my case, the default is"/var/lib/mpd/tag_cache)"
I've uncommented the line: bind_to_address    "localhost", because it does not make sense to initiate wifi music from outside the place where the music setup is.

MPD needs to be ebabled: "systemctl enable mpd". Then to start the mpd server type "systemctl start mpd" and to stop it type "systemctl stop mpd".
Two decent MPD clients for android smartphones are MPDroid and M.A.L.P. I found the display of the former a bit loud, so I've chosen the latter. Both are easy to install and to use.

MPD extracts information contained in the headers of the MP3 files to determine the artist name, album name, track name and number and artwork. Often this information turned out to be partially unavailable or incongruous. When searching for the music of the fab 4 for example, some of it was to be found under the artist name "Beatles" while other albums were attributed to "the Beatles". "Franz Listz" and "Listz" are also different artists. The compositions of John Sebastian Bach could only be found under the names of the artists who performed his works. A complete mess! Hence the real hard work started then: In the course of two weeks I edited thousands of headers with a freeware tool, MusicBrainz Picard.


 
Adding the DAC
to top of page    Published: 1/2/2021

Because the sound quality of the standard RPi audio output is not extremely good, a quality Digital to Analog Converter (DAC) that branches onto the Raspberry is recommended. There are several DACs available. I've chosen the (33€) "DAC HAT" from Inno. It connects directly to the Raspberry Pi board. The DAC uses some of the GPIO pins, the remainder can still be used for other purposes. See the manual for particulars.

Then the I2S audio drivers on the RPi need to be enabled. To do so type "sudo nano /boot/config.txt" to edit the config.txt file.
Uncomment the line "dtparam=i2s=on" in the hardware interface section.
Append the following at the end of the config file: "dtoverlay=allo-boss-dac-pcm512x-audio".
Save the config.txt file and reboot the RPi ("sudo reboot"). The DAC HAT needs to be plugged onto the RPi when you do that.
Connect the DAC's audio output to the stereo sound amplifier (e.g. in the aux input).


 
A web-based music player
to top of page    Published: 5/2/2021

For the project described above I've put a library of mp3 files on the Raspberry's hard disk. Since the Rasberry has a web server, it is possible to listen to these files over the internet using a web page as an interface. In this section I'll describe a simple solution to privately listen to your music. It is not suited for multiple users (although amendments could probably be made to make that possible too).

diagram
My provider's web server is an intermediate bewteen my smartphone and the RPi as shown in the figure to the left. The RPi has a folder that is accessible from the internet and a music folder on an external hard disk owned by the the Music Player Daemon which is protected from the internet.
The content of the pages that appear on the smartphone are generated by
1) C++
2) php
3) Javascript
4) HTML5

To start with the first, a database and search routines are made with C++ for speed of execution as the code is compiled of course. All the souce code is available for download here. The structure of the database is pretty straightforward. It contains a list of artist which each contain a list of albums containing each a list of tracks.
Once the database of class Mp3Base is made it is not complicated to search the database lists using the routines FindArtist() and FindAlbum().
Creating the database is the most difficult task because the headers of all the mp3 files in all the folders within the main "music" folder need to be parsed and their information retieved.To not complicate things even further only mp3 music files are considered here. The latest mp3 header format, ID3v2, is documented in detail on the id3.org site. Later versions to appear in the future should remain downward compatible. The mp3 header is subdivided into "frames". Each frame has a unique name for which one can search e.g. "TALB" stands for album title and "TIT2" is for songname. Each frame name is followed by a standard number of "flags" and a number giving the size of the frame, usually the length of a character string. The following snippet from the file makebase.cpp gives an idea of the approach that I used. Some of the text is in pseudocode to make it more readable. First the routine SearchMp3Directory() declares a DIR structure defined in <dirent.h>. It then searches this structure for files with the *.mp3 extension. It then reads in the first 4000 bytes in a char string called here "folder1". Now it searches this string for the occurrence of tags. Only the "TPE2" is shown here. The routine SearchWhat() points to the beginning of the frame tag that has been found. Immediately after the tag follows a 4 byte field giving the tagsize in a funny format. Funny in that each byte ranges from 0 to 16 rather than 255, hence the strange way of deciphering these 4 bytes. Finally the text string can be read and stored in the database. Reading other tags needed goes similarly.

The main page that shows on the phone gives a list of artist's names:

screen

This page, music.php, contains nothing except an <iframe>. This iframe has jukebox.php as its source. This is done to keep the page full screen and prevent it from returning to normal view when the page contents are refreshed. It is impossible to go full screen on page load, since it requires user intervention. This is done here if the user clicks any element (i.e. link or gadget) on the page using the Javascript function, parent.document.documentElement.requestFullscreen(). "Parent" being music.php, which never needs to be refreshed.
Each artist name contains a link to the php script GetList.php e.g.:
href='http://your raspberry web name:443/GetList.php?command=album&name=Albert Ammons'
"command=album&name=Albert Ammons" means: create the list of albums of Albert Ammons.

The GetList.php script on the RPi calls the C++ program, "getlist":
exec("/usr/share/your C folder/getlist ".$command." '".$name."'",$out).


A solution to come to more or less the same result exists in jquery: $.get('http:// etc ').
However calling this java function does not wait for the execution of the c++ program to finish, while the php command "exec" does. Hence, with the php script one can be sure that the result is available for use.
Upon execution of getlist, the GetList.php script returns to the jukebox iframe with
"header('Location: http://juke.bram.org/jukebox.php?reload=1')". This reloads the page on the phone with the new information i.e. a list of albums.
Clicking an album similarly returns a list of tracks.

 

tracks

Jukebox.php has written out a java script section for each track. The example of track 5 is:

The filenames are stored in an array of char strings, audionames[], declared in the javascript section at the beginning of the page. When <div id='A4'> is clicked, the javascript function PlayFrom(4) is called.  The function PlayFrom(num) is uses the variable 'sound' declared as var sound=document.createElement('audio'), which is the dynamic variant of the HTML5 tag "<audio>". The function sound.addEventListener('ended') detects the end of the mp3 file and calls PlayFrom(num+1) until the end of the album (= the end of the audionames array) is reached.
Note that PlayFrom() calls the jquery function $.get('http://your raspberry web name:443/CopyMp3.php?file='+file).
CopyMp3.php is a script on the RPi that ask for the mp3 fime on the hard disk to be copied to the /var/www/html/ folder on the RPi. The mp3 filename in de html folder is also changed into mp.mp3. Because CopyMp3.php uses the exec() function it returns only after the file copy is entirely carried out. I've done many tests using different web sites, but contrary to other web sites, the raspberry get calls allways return false. Hence the fail() function doing exactly the same as the function on succesful return i.e. setting the source attribute of the audio element to:
sound.src="http://your raspberry web name:443/mp.mp3";
sound.play();

All php and java scrips used are available here

The volume of the sound is changed using the volume buttons on your smartphone.

artists leads to the artist page
back goes to the last artist consulted
forward goes one track further.
back recedes one track