gmusicradio

Background

Radio

For a long time in my life, I preferred to listen to music by turning on the radio. Over the years, any given radio station would either always play only new or only old songs. Some stations stopped sending news during the evening times which are currently my primary times of listening. Additionally, none of the existing radio stations seemed to fit the increasing variety of music taste. After unsuccessfully trying to find a station that would satisfy my needs, I started looking for alternatives to radio.

No Streaming Service

The primary way to listen to music in 2023 seemed to be the use of Music Streaming Services (e.g. Spotify). Streaming is similar to radio: Audio quality is often mediocre (except for more expensive offerings like Tidal and Qobuz). Additionally, with streaming (like with Radio) one doesn’t “own” the music in any way. Specifically, some much-liked music may disappear overnight from the service if they decide to no longer license it (or are forced to remove it for whatever reason).

Unlike public radio which are covered by Rundfunkbeitrag in Germany, most streaming services cost a monthly fee or deliver an excessive number of ads in addition to making money from the user data by means of analytics. Ads are an issue with (especially privately-owned) radio stations, too, but data protection issues like tracking and analytics are of no concern to radio listeners. Effectively, swichting to streaming would have meant to incur a monthly fee for licensing the high-quality no-ads offering.

While this would have certainly been a valid means to upgrade from the “free” radio listening experience, it would have also meant to spend a fixed amount of money monthly and to add yet another dependency on the online connection (I do not count the various “offline modes” of programs to be valid substitutes for the real thing). Unlike with other subscriptions, there is nothing to “keep” when cancelling such a service – all data is typically in proprietary, DRM’ed formats – it is basically an infrastructure cost.

Offline Music Collection

Due to these concerns, I was relucant to subscribe to a streaming service and instead pursued the “old way” of building a music collection. The major advantage of this approach is that building a collection allows stopping payments (except for storage if you are pedantic) at any point in time without losing any of the existing music. Over time, the costs are mostly due to the addition of new music to the collection and could hence even decrease as the collection builds-up. Better data protection and independence from online sources and the existence of a particular service are additional benefits.

Getting started with a music collection in 2023 was much easier compared to earlier times due to multiple favorable circumstances:

A lucky way to bootstrap a collection from almost zero (1 CD + a few FLACs from Qobuz) was the opportunity to start from a stranger’s music collection for free. He told me that he no longer owned any playback equipment for CDs and that they had been sitting in the basement for years. Sometimes, collections from similar situations are also offered for sale, but given their often strong emotional value to the owners, they often do not exactly offer good value for the money.

The Quest for Playback Software

Once the music collection started to take shape, (digital) organization and playback of the collection became a real concern. Having owned only a few music files before, my go-to playback applications were mpv and mocp none of which seemed up to the task of playing back and managing a “real” music collection with significantly more than 100 songs.

To my surprise and unlike with CD ripping, music collection management and playback seem to not have advanced much beyond the times when music streaming wasn’t really a thing. To me, much of the software programs, features and workflows looked like being stuck in the 2000s.

Specifically, I’d have expected the following things to have been resolved by now but they apparently aren’t:

I checked the following playback applications to see whether they would suit my unusual requirements (see points before):

Player/Homepage Version Documentation Git(hub) Debian
aqualung master/b9e3f5ccd manual jeremyevans/aqualung removed for unmaintained
gmusicbrowser 1.1.99.1 guide squentin/gmusicbrowser removed for unmaintained
quodlibet 4.5.0-2 features quodlibet/quodlibet quodlibet

Notes about these players:

Additionally, I took the following ones into consideration, but did not test them so far. This list of links is mostly for my own notetaking :)

gmusicbrowser

After evaluating some of the playback applications, gmusicbrowser seems to provide the closest match for my use cases: It supports all of the standard features and (experimental) gapless playback, bit-perfect playback (when configured with gstreamer backend, ALSA device hw:..., see https://www.head-fi.org/threads/bit-perfect-audio-from-linux.561961/#post_7596563), provides a highly customizable user interface and allows IPC using the gmusicbrowser command and DBUS. It is one of the few applications that support randomized playback weigthened by user-selected criteria.

gmusicbrowser screenshot

The screenshot shows gmusicbrowser when controlled by gmusicradio. This is not directly visible, but the queue is automatically populated by gmusicradio and thus likely to contain a single song most of the times (as in the screenshot).

gmusicbrowser randomized playback configuration

This screenshot shows a configuration for the randomized playback that attempts to prefer better-rated and lesser-played songs (criteria in decreasing order of importance).

A randomized playback using this feature is better than an all-random playback but still suffers many of the issues wrt. randomized playback: It is just not “fair” and often rather plays the same song again rather than chosing another one that is available. If playback count were to be rated stronger, this would automatically cause the highly-rated songs to be played back less often.

Hence even with this advanced feature, the integrated playback mechanism still seems to be insuficcient. Additionally, gmusicbrowser does not support news playback out of the box and a hacky attempt to bolt-on that feature works only partially (cf. run_podget.sh).

Warning

This repository provides a snapshot of what I did at the time. In the meantime I decided to pursue building my own player based on MPD and some of the ideas (algorithm and even code) provided in this repository.

This also means that this repository’s contents are likely to stay static after initial publication and may or may not work for your use case but are unlikely to get large fixes.

Abstract

This repository provides gmusicradio.erl – a script that can be used to control the playback of a running gmusicbrowser instance in a radio-inspired way but for personal listening on a local Linux machine.

At the core, this consists of the following two key rules:

Once started, gmusicradio runs in parallel to gmusicbrowser and automatically enqueues new songs to play as the playback in gmusicbrowser continues. This means that you can safely pause the playback for a short while and continue later as the script just waits a little longer for the playback of the song to complete. Also, you (acting as a DJ) can enqueue your own songs of choice and the script ignores them such as long as afterwards, it recognizes the playback of a song that it enqueued itself.

Metadata is entirely maintained by gmusicbrowser and gmusicradio reads the gmbrc of gmusicbrowser to get to know the initial values of playback counters and ratings. It then keeps an in-memory copy and locally tracks the playback conunters under the assumption that its enqueued songs were the only actions to affect this data. You can safely exit gmusicradio at any time to take full control of the playback and re-start it later. In addition to the stars rating, the algorithm uses the playback count as a secondary criterion and is thus not going to create the same schedule again even if it were cancelled just after having played a few songs.

Prerequisites

To make use of this script, you need the following two logical prerequisites:

As a software dependency, an Erlang OTP runtime is required (e.g. Debian package erlang).

Setup the configuration of gmusicradio (see section Configuration) and then run the script as ./gmusicradio.erl. You can also build a Debian package for it by running ant package.

Configuration

gmusicradio looks for a file called .mdvl/gmusicradio.xml in your home directory. The following shows a file equivalent of the default settings:

<?xml version="1.0" encoding="UTF-8"?>
<gmusicradio
    podcast_conf="/data/programs/music2/supplementary/news/conf"
    podcast_dir="/data/programs/music2/supplementary/news/pod"
    podcast_timeout="30000"
    podcast_chck="10"
    gmbrc="/home/linux-fan/.config/gmusicbrowser/gmbrc"
    await_ms="60000"
    cmd_timeout="10000"
    schedule_len="60"
    chaos_factor="2.0"
    initial_factor="0.5"
    min_good_perc="30"
    default_rating="60"
/>

If any of the attributes are left out, their default values are used. For the gmbrc, the default is not actually /home/linux-fan but rather the user’s home directory hence the minimum options that need to be adjusted to your installation are podcast_conf and podcast_dir, all other values can (but need not be) changed.

Podcast Configuration

podcast_conf
This points to the directory containing podgetrc and serverlist. These files are processed by running podget – gmusicradio does not currently process them directly. See below for example values for these files.
podcast_dir
This points to the directory that is usually given as DIR_LIBRARY in the podgetrc. gmusicradio scans this directory for new .mp3 files to identify that a new podcast episode was downloaded and then enqueues it for playback. Note that in theory this path could be found automaically by gmusicradio (it is encoded in the podgetrc), but this is just not implemented for now and hence users need to specify this path.
podcast_timeout
Configures the timeout in milliseconds that an invocation of podget is expected to take (at most). The default should be OK unless your Internet connection, the podcast server or your local machine are slow. In such cases it may make sense to increase this timeout.
podcast_chck
Configures the interval at which podget should be called. It is given as a multiple of await_ms i.e. the default of 10 with await_ms of 60000 means every 600000ms = every 10 minutes. This should be OK for many cases, but if your news are only every two hours you could also increase this to e.g. 60 to check for news about once every hour.

Example podgetrc

CONFIG_SERVERLIST=serverlist
DIR_LIBRARY=/data/programs/music2/supplementary/news/pod
LOG_FAIL=errors
LOG_COMPLETE=done
WGET_BASEOPTS="-c -nH"
MOST_RECENT=1
FORCE=0
CLEANUP=1
CLEANUP_DAYS=1
MIN_SPACE=614400
NO_PLAYLIST=0
PLAYLIST_NAMEBASE=New-
DATE_FORMAT=+%F
ASX_PLAYLIST=0
FILENAME_BADCHARS="\`~!#$^&=+{}*[]:;\"'<>?|\\"
FILENAME_REPLACECHAR=_
FILENAME_FORMATFIX=1
FILENAME_FORMATFIX2=1
FILENAME_FORMATFIX3=0
FILENAME_FORMATFIX4=0
FILENAME_FORMATFIX5=1
FILENAME_FORMATFIX6=1
FILENAME_FORMATFIX7=1
FILENAME_FORMATFIX8=1
FILENAME_FORMATFIX9=1
FILENAME_FORMATFIX10=1

Example serverlist

https://www.deutschlandfunk.de/nachrichten-108.xml deutschlandfunk

Other Configuration

Typically, the default_rating and min_good_perc values could be of interest for user adjustments.

gmbrc
Configurs the path to the gmusicbrowser config file. gmusicradio parses the [Songs] section of that file to find your music, ratings and initial play counts (per startup). By default, gmusicbrowser computes the path from the user home directory hence the default may be OK for many cases already.
await_ms
Configures the interval in milliseconds to query gmusicbrowser for its currently playing song. This should be significantly shorter than the shortest song in your library because it must have a chance to reliably “see” each song as it is being played-back because that is the trigger for gmusicradio to enqueue the next song of the schedule. The default should be OK for Pop and Rock songs but may need some adjustments for other genres.
cmd_timeout
Timeout in ms for a call to gmusicbrowser to return. When no instance is running, a gmusicbrowser may automatically be launched by gmusicradio and this will cause the process to not exit (quickly). If this timeout is observed, it is thus not an error from the point of gmusicradio and rather indicates that it just started the gmusicbrowser instance. Thus it is recommended to set this to a value such that it is highly unlikely for a regular enqueue action to take so long (usually it takes less than 1 sec on my machine) but the value should be short enough such that the follow-up action can quickly take place if the gmusicbrowser window was lanuched by the enqueue action. The default of 10sec should be OK for many cases, but you can of course experiment with shorter intervals like e.g. 5000 for 5sec.
schedule_len
Configures the length of a schedule (aka. playlist) to generate by gmusicradio. The value must be lower than the total number of songs in the database. In principle, the schedule has no upper limit if your music library is large enough, but there is also little advantage to make this needlessly large because gmusicradio automatically generates a new schedule once it has finished playing the current one. The default should thus be OK except for the following cases: If you have only a few songs, you may need to reduce this value. If you want to play all day without repeating any songs with three-stars or less, you can increase this value accordingly.
chaos_factor
The chaos factor affects the randomness in the playlist generation. It ranges from 0.0 upwards. A value < 1.0 ensures that the schedule always prefers the songs with lower play count. A value of 0.0 disables randomness in the playlist order (and instead follows the index values defined by gmusicbrowser). Values > 1 allow (with certain probability) for songs to be played even if they are not due for this yet per their play counts. This may increase the perceived randomness of the playlist. In many cases, the default of 2.0 should be OK.
initial_factor
The algorithm works by merging multiple shuffled lists. If this were to be executed in a purely “correct” manner, the result playlist would always start with one song for each rating. This overrepresents very nice (5 star) and very badly rated (2 star) songs by adding one of each to the beginning of the playlist. As a work around, this factor tricks the system into believing that it has already added at least one (or by default “half of a”) song of each rating to the playlist. Set this to 0 to disable this workaround. Set it to one to suggest to the system that one song has already been added for reach rating. Never set it to a higher number than the number of songs in the most rarely set rating group (unless you want to distort your playlist in funny ways like e.g. playing that rating group after all other songs and only if playlist size is close to the total number of songs).
min_good_perc
When the music collection contains many songs with rating of 3 stars or less which is e.g. expected when it is sourced from many albums that often contain only a few “favorite” tracks and a lot of “ok-ish” songs then there is a shortage of songs with four or more stars. This is quite normal and the standard radio approach to fix this is to play these “favorite” songs more often compared to the other ones. The min_good_perc ranges from 1 to 90 and is used to configure this repeated playback behaviour: It defines a guideline percentage of 4+-stars songs to play and defaults to 30%. If too few well-rated songs exist, the script repeats the “good songs” playlists until it risks surpassing this percentage. IOW: The actual playback of good songs is usually going to be less than the given percentage, but it is as close to it as possible without “unfairly” repeating one song more often than another. In practice, this fairness effect can only be noted with large schedule lengths. The practical effect of this value is that higher values cause more of your favorite songs to be played at the expense of giving a “less interesting” overall programme as it is going to repeat this limited “best” songs more often. On the other hand, setting this to a low value eventually causes the good songs to not be repeated any more than all other songs and results in a very fair distribution. The default of 30% sounds good to my ears with the collection natively providing 7.5% of 4+-stars songs (see example output further down).
default_rating
This setting defines the rating for “unrated” songs. It is expected that you set it to the same value as in gmusicbrowser (in my case that is 3 stars corresponding value 60) but you can of course also deviate to achieve different playback between gmusicbrowser and gmusicradio. The value could in the future be automatically retrieved from the gmbrc but this is currenlty not implemented.

Example Output

The following shows a truncated output of running gmusicradio with configuration initial_factor="0.0". Some of the details about the output format are explained in this section.

$ gmusicradio.erl
Ma_Sys.ma gmusicradio 1.0.0 (c) 2024 Ma_Sys.ma <info@masysma.net>
Read GMBRC... OK
Distribution of stars
 1 Star     91 (ignore)
 2 Stars   104 ( 2.89%)
 3 Stars  3228 (89.59%)
 4 Stars   271 ( 7.52%)
 5 Stars     0 ( 0.00%)
Duplicate = 3
Schedule of 60 songs:
I  279 R80 C    0 Linkin Par Meteora              Easier to Run        (2003)
I 2681 R60 C    0 Rihanna    Unapologetic         Jump                 (2012)
I  617 R40 C    0 Sheryl Cro Tomorrow Never Dies  No One Said It Would (1997)
I 2740 R60 C    0 Razorlight Slipway Fires        Monster Boots        (2008)
[...]
I 2265 R60 C    0 Mary J. Bl No More Drama        Love                 (2001)
I 2076 R80 C    1 Coolio     Gangsta’s Paradise   Gangsta’s Paradise   (1995)
I 3498 R60 C    0 Sugababes  Angels With Dirty Fa Angels With Dirty Fa (2003)
I 1358 R60 C    0 The Police Their Greatest Hits  Every Little Thing S (1990)
ENQUEUE Linkin Par Meteora              Easier to Run        (2003)
AWAIT   Linkin Par Meteora              Easier to Run        (2003)
AWAIT   Linkin Par Meteora              Easier to Run        (2003)
AWAIT   Linkin Par Meteora              Easier to Run        (2003)
AWAIT   Linkin Par Meteora              Easier to Run        (2003)
AWAIT   Linkin Par Meteora              Easier to Run        (2003)
FOUND   Linkin Par Meteora              Easier to Run        (2003)
ENQUEUE Rihanna    Unapologetic         Jump                 (2012)
AWAIT   Rihanna    Unapologetic         Jump                 (2012)
AWAIT   Rihanna    Unapologetic         Jump                 (2012)
AWAIT   Rihanna    Unapologetic         Jump                 (2012)
AWAIT   Rihanna    Unapologetic         Jump                 (2012)
AWAIT   Rihanna    Unapologetic         Jump                 (2012)
FOUND   Rihanna    Unapologetic         Jump                 (2012)
ENQUEUE Sheryl Cro Tomorrow Never Dies  No One Said It Would (1997)
AWAIT   Sheryl Cro Tomorrow Never Dies  No One Said It Would (1997)
AWAIT   Sheryl Cro Tomorrow Never Dies  No One Said It Would (1997)
AWAIT   Sheryl Cro Tomorrow Never Dies  No One Said It Would (1997)
AWAIT   Sheryl Cro Tomorrow Never Dies  No One Said It Would (1997)
[...]

Distribution of Stars

gmusicradio parses the gmbrc and prints what it recognized from that config in the form af a Distribution of stars. This is a nice statistic and allows you to quickly identify if it picked the wrong config file, if the default rating is set wrongly or other errors of that kind. The numbers may also be useful for adjusting the Configuration settings.

Note that 1-star rated songs (and 0-star if you use that distinction) are ignored by gmusicradio. The idea is that there may be songs that you don’t want to hear (or not hear in the automatic playlist) but still want to keep on disk for reaons.

Based on this distrubtion and on min_good_perc gmusicradio computes a number of times to repeat the 4+-rated songs. The resulting repeat factor is printed out as “Duplicate” number. In the example, the computation is as follows:

Schedule

Each line in the schedule is formatted as follows:

Playback

The actions by the script are shown by all-caps states:

This repeats until the schedule is complete after which a new schedule is printed.

Other Repository Contents

File run_podget.sh contains a script that was used prior to gmusicradio to attach podcast playback to gmusicbrowser. It requires that the gmusicbrowser is already running and expects the podcast configuration and library directories to be placed next to the script in a specific way (see its source code).

It works OK in conjunction with the randomized playback but fails if e.g. playback in sequence is wanted because then gmusicbrowser always jumps to a specific position after the news playback.

The script can serve as an additional inspiration for users intending to perform their own gmusicbrowser automation.

There are additional parameters to gmusicradio that can be used to generate M3U playlists or print a list of all albums in the gmbrc. Refer to the script’s source code for details.

Future Directions

Development efforts continue into the direction of using MPD with a customized client because this makes more sense for the more complex use cases that have become relevant in the meantime.

See Also

Relevant Programs

Webradio

mpv https://radiogroup-stream32.radiohost.de/radio-frankfurt_mp3-192
mpv http://streams.radiobob.de/bob-wacken/mp3-192/streams.radiobob.de/
mpv --cache=yes --cache-secs=4 --demuxer-max-bytes=$((1024 * 1024 * 10)) http://motherearth.streamserver24.com:18910/motherearth.klassik

Ma_Sys.ma Website 5 (1.0.2) – no Flash, no JavaScript, no Webfont, no Copy Protection, no Mobile First. No bullshit. No GUI needed. Works with any browser.

Created: 2024/01/06 15:56:06 | Revised: 2024/06/01 14:32:28 | Tags: gmusicradio, song, music, gmusicbrowser, radio, playlist, schedule | Version: 1.0.0 | SRC (Pandoc MD) | GPL

(c) 2024 Ma_Sys.ma info@masysma.net.

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.