Musly Integration for the Logitech Media Server

screenshot 1

Musly is

a program and library for high performance audio music similarity computation. Musly only uses the audio signal when computing similarities. No metadata is used in the similarity computation process.

LMSmusly makes this functionality (comparable to MusicIP) available for LMS.

Features

  • Finds music similar to a specific track and creates a static or dynamic playlist in LMS
  • Implemented as a stand-alone CLI application
  • Web interface
  • Integration with extGUI4LMS

Status

  • Musly itself seems stable, and preliminary testing suggests the quality of the results is excellent. It is also very fast, both at analyzing the music, and at finding tracks.
  • LMSmusly is feature complete, but beta quality ( i.e. there may be quality issues)

Installation

libmusly v0.2

needs to be installed (specifically, the files libmusly.so and libmusly_resample.so) to the location /opt/musly-0.2/lib
If your platform is listed below, you can download a pre-built binary version, otherwise you have to build it yourself.

Binaries

First, make sure ffmpeg is available, since musly depends on it. If not, install it with your OS package management. Note that the binaries have been linked against ffmpeg 3.4.1 - if your version is too old, you'll have to upgrade it (e.g. see this ppa for Ubuntu LTS), or build your own (since only the audio part is required, you could compile a stripped-down version)

For x86 platforms (Intel, AMD), run cat /proc/cpuinfo | grep avx if you are unsure if your CPU supports AVX instructions.

  • Linux x86_64 AVX, ffmpeg linked dynamically: download
  • Linux x86_64 no AVX, ffmpeg linked dynamically: download
  • Linux arm64 / aarch64, ffmpeg linked dynamically: download
Building from source

Note: only necessary if you haven't installed a binary version already.
Building libmusly is pretty straightforward. Get the master branch from github and follow the instructions there.

Example

Install ffmpeg with your package manager.
Get sources and unpack:

    wget https://github.com/dominikschnitzer/musly/archive/master.zip
    wget http://bitbucket.org/eigen/eigen/get/3.3.4.tar.bz2
    unzip master.zip
    tar -C /tmp -xf eigen-eigen-5a0156e40feb.tar.bz2

Edit musly-master/CMakeLists.txt:

    add_definitions(-DMUSLY_VERSION="${MUSLY_VERSION}")
    # start additions
    SET(CMAKE_INSTALL_PREFIX "/opt/musly-0.2")
    SET(CMAKE_BUILD_TYPE "Release")
    SET(CMAKE_SKIP_INSTALL_RPATH  FALSE)
    SET(CMAKE_SKIP_RPATH  FALSE)
    SET(EIGEN3_INCLUDE_DIR "/tmp/eigen-eigen-5a0156e40feb/")

    SET(CMAKE_SKIP_BUILD_RPATH  FALSE)
    SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 
    SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
    # end additions

Run cmake and make:

    mkdir build; cd build
    cmake ../musly-master
    make -j4
    sudo make install
LMSmusly

is implemented in Python and requires

  • python 3
  • python requests
  • cherrypy, if you want to use the WebUI. Note: needs a fairly recent version ( tested with 13.1.0, <= 3.8 known not to work)

Most likely, these packages are available from your OS package management (e.g. python3,python-requests and python-cherrypy in Debian, dev-lang/python, dev-python/requests and dev-python/cherrypy in Gentoo etc.). Or if you prefer, you can use pip with a virtual environment:

    python3 -m venv ~/python-venv
    source ~/python-venv/bin/activate
    pip install requests
    pip install cherrypy

Remember to execute source ~/python-venv/bin/activate once per session before running lmsmusly.py

source distribution

The latest version is available here. Download and extract anywhere. It should run without installing it (or you could try installing it with the included setup.py (read this first if you are unfamiliar with setuptools)).

Example (adjust for version):

    wget "https://www.nexus0.net/pub/sw/lmsmusly/dist/LMSmusly-0.1.1.tar.gz"
    tar xf LMSmusly-0.1.1.tar.gz
    cd LMSmusly-0.1.1/src/
    ./lmsmusly.py -h
built distribution for python 3.6

The latest version is available for 64 bit Linux. This is basically an archive to be extracted to / (i.e. the root of the filesystem). Note: not suitable for Debian/Ubuntu without manual adjustment of the location of Python site packages.

Usage Instructions

First, the music collection needs to be analyzed. Once this is done, playlists can be created either statically (i.e. with a fixed length), or the application can keep adding new songs when the player reaches the end of the playlist.

For playlist creation, a track is needed to compute the similarities (the so-called seed track). Default is to use the first track in the current playlist.

Run lmsmusly.py -h to get help.

Modes of operation
Options for all operations
Option Description Example
--db-path Database path. Required. --db-path /home/user/musly
--log-level set log level --log-level WARN
--pid-file write PID to a file --pid-file /var/run/lmsmusly.pid
analyze

Analyzes music files and writes the results to the database. The paths to the music files must match those in the LMS library, or the tracks will not be found. If you are running the analysis on a different machine, use the --replace-path option.

Example: lmsmusly.py --db-path /home/user/musly analyze --replace-path /home/user/tmp/sshfs:/data/music /home/user/tmp/sshfs

There are two options (--extract-length and --extract-start) determining which part of the track is analyzed. Defaults should be fine. From the musly documentation:

  • excerpt_length: The maximum length in seconds of the excerpt to decode. If zero or greater than the file length, decodes the whole file. Suggested value: 30.
  • excerpt_start:The starting position in seconds of the excerpt to decode. If zero, decoding starts at the beginning. If negative, the excerpt is centered in the file, but starts at -excerpt_start the latest. If positive and excerpt_start + excerpt_length exceeds the file length, then the excerpt is taken from the end of the file. Suggested value: -48.

Run lmsmusly.py analyze -h for a full list of options.

writejukebox

If there are a large number of tracks in the database, the startup time of lmsmusly increases significantly. To avoid this, lmsmusly can write a so-called jukebox (which is basically an initialized version of the similarity component) to the file system, and read it the next time it is started.
Example: lmsmusly.py --db-path /home/user/musly writejukebox will write the jukebox to /home/user/musly/jukebox.bin

Then, when creating a playlist, you can use the --read-jukebox option:
Example: lmsmusly.py --db-path /home/user/musly static --read-jukebox --lms lmsserver --lms-player 00:11:22:33:44:55 50 will create a static playlist

The only limitation this method has is that the jukebox file must match the content of the database when it was created. If files are analyzed after the jukebox was written, it will have to be recreated.

static

Creates a playlist with a fixed length starting from one track, then exits. The track can be specified either by having it in the playlist of the target player (default is to take the first track), or by passing the file name as a parameter.

Example: lmsmusly.py --db-path /home/user/musly static --lms lmsserver --lms-player 00:11:22:33:44:55 50 will add 50 tracks to the playlist of player 00:11:22:33:44:55 (based on the first track of the current playlist)

Run lmsmusly.py static -h for a full list of options.

dynamic

Creates a playlist with a defined length starting from the first track of the current playlist, and keeps adding new songs when the player reaches the end of the playlist. Note that the application will never exit, but can be stopped using the web interface, or killed safely (with ctrl-c or by sending SIGINT or SIGTERM to the process).

Example: lmsmusly.py --db-path /home/user/musly dynamic --lms lmsserver --lms-player 00:11:22:33:44:55 will continue adding tracks to the playlist of player 00:11:22:33:44:55 (based on the first track)

Run lmsmusly.py dynamic -h for a full list of options.

Web Interface

A web interface can be enabled by passing the --webui option. By default, it is available at http://hostname:8080

Duplicate tracks

If you don't want tracks being queued more than once in a session, use the --no-duplicates option, which will avoid adding the same track for a number of tracks (default 100).

Similarity

The default is to find the (length of playlist, --numtracks (default 10)) most similar tracks, which are added in descending order. Since by default the first track of a playlist is used as a seed, this will result in the most similar tracks being added for a session. If you want more variety, there are 2 options:

  • use the --seedtrack-index option to select a less similar track as the seed track for the next tracks to be added
  • use the --topresults-factor option to adjust results selection. Normally, the top (length of playlist) tracks are calculated and added. With this option, the the top (length of playlist)*(topresults-factor+1) tracks are calculated, and tracks are added from an offset (length of playlist)*(topresults-factor). Example (length of playlist=10, topresults-factor=2): The top 20 tracks are calculated, and tracks 11-20 are added.
info

Shows information about libmusly and database statistics.

extGUI4LMS Integration

If LMS Musly is running in dynamic mode with the --webui option, extGUI4LMS can display similar songs to the one playing / selected.

The URL to access it must be configured in Preferences > Technical Parameters > App_muslyURL in this format: http://hostname:8080/api

After a restart of the extGUI4LMS web application, results will be available in Currently Playing / Selected Track > More > LMS Musly.

Note: use the --start-paused option if you only want LMS Musly functionality in extGUI4LMS without it modifying the playlist.

FAQ

The music analysis crashes with a 7716 Illegal instruction (core dumped) error. Why?
Your CPU doesn't support certain instructions (AVX, SSE4), either because it is very old (<2008 (SSE) / < 2011 (AVX) ), or low-end (some Atoms / Celerons / Pentiums). Use the non-AVX version of libmusly.
Is it possible to run the analysis on a different machine then the one used to run the application afterwards?
Yes, if the paths to the music files are identical (or the --replace-path option is used), simply copy the lmsmusly.db file to the target.
Is it possible to change the paths to the music files after the analysis?
Yes, lmsmusly.db is a sqlite3 database, so it can be processed with SQL easily.
Is it possible to steer the dynamic mode?
The selection of the next tracks is always based on the first in the playlist (unless --seedtrack-index is used), so moving a different track to the first position will change the next results. Note, however, that a seed track will not be used more than once during one session (to avoid loops).

API

If the application is running in dynamic mode with --webui, the following commands are available:

http://host:8080/api?cmd=lm-pausepauses the application
http://host:8080/api?cmd=lm-unpauseresumes the application
http://host:8080/api?cmd=lm-exitexits the application
http://host:8080/api?cmd=lm-getstatsreturns statistics (as JSON)
http://host:8080/api?cmd=lm-getsimilar&path=/data/music/song.mp3&numres=10returns the numres most similar tracks (as JSON)

License

GNU GPLv3

Reporting Issues

Use the discussion thread in the LMS forum.