Liquidshop 4 Presentation (updated)
Matthias C. Hormann (“Moonbase59”)
2024-06-17
On-the-fly JSON song cue-in, cue-out, overlay, replaygain calculation for Liquidsoap, AzuraCast and other AutoDJ software.
Phew! Now what does that mean?
On the AzuraCast GitHub, the “Professional Crossfade” thread is the single most active discussion.
There is a huge user demand for radio-like, more professional song
transitions!
John Warburton (“Warblefly”), an industry professional and Tonmeister, already talked about “Easing automation and improving your sound with Liquidsoap and FFmpeg” in 2021 (Liquidshop 1).
He also made available his pre-processing and playlist annotation scripts. Thanks for sharing, John!
Inspired by John’s work, I started writing cue_file
in
early February 2024, as a proof of concept, to see if “on-the-fly”
processing could be done.
cue_file
is a Python3 script, that in turn uses
ffmpeg and ffprobe to analyse an audio file for cueing
and transition data, based on the loudness perception of the human ear.
It uses the EBU R.128 algorithms and returns JSON data.
Many talks and tests with RM-FM, toots and Stefan (gAlleb) brought up two solutions:
cue_file
.%include
or copy-paste (AzuraCast).In this presentation, we will concentrate on autocue.cue_file.
Let’s visualize what autocue does.
Long smooth endings will be kept intact.
Limits overlay length (too long sounds bad).
Smooth, continuous playout, radio-style.
And perfect transitions—everytime.
Concepts, units, and inner workings explained.
With visual examples.
Let’s make it easy and just say:
+10 dB | 2x perceived loudness (psycho-acoustics) | mostly sensed |
+6 dB | 2x sound pressure (RMS voltage, amplitude) | mostly measured |
+3 dB | 2x intensity (power, energy) | mostly calculated |
We have to be specific in acoustics!
Queen: Bohemian Rhapsody
liq_loudness
,
liq_loudness_range
, liq_true_peak
settings.autocue.cue_file.silence
liq_cue_in
).liq_cue_out
).Nirvana: Something in the Way / Endless, Nameless
liq_cue_out
,
liq_blank_skipped
.settings.autocue.cue_file.overlay
liq_cross_start_next
.settings.autocue.cue_file.longtail
settings.autocue.cue_file.overlay_longtail
liq_longtail
shows if a long tail was detected.settings.autocue.cue_file.fade_out
liq_fade_out
settings.autocue.cue_file.target
settings.autocue.cue_file.unify_loudness_correction
liq_amplify
,
liq_reference_loudness
, replaygain_track_gain
,
replaygain_reference_loudness
.settings.autocue.cue_file.noclip
liq_amplify_adjustment
.That’s much easier. Promised.
Because autocue does all the work for you.
Using standalone Liquidsoap
Copy cue_file
to appropriate location in the path.
On Linux, this is usually one of these:
~/bin
~/.local/bin
/usr/local/bin
(needs sudo
)Ensure you have Python3, ffmpeg and ffprobe available. On almost all distros, these are pre-installed.
# Minimal example for the `autocue.cue_file` protocol.
# Uses one playlist and outputs to sound card.
%include "autocue.cue_file.liq"
# Ensure AutoCue settings are valid
ignore(check_autocue_setup(shutdown=true, print=true))
enable_autocue_metadata()
radio = playlist("Classic Rock.m3u")
radio = amplify(1., override="liq_amplify", radio)
radio = crossfade(radio)
radio = mksafe(radio)
output(radio)
Since 2024-05-21, AzuraCast Rolling Releases have autocue.cue_file included, ready to use!
Switch it on in Edit Station Profile → AutoDJ:
I put all settings in, so I don’t have to look them up.
Thanks for following so far!
More Tech and a Question & Answer section follow in
Part II
It’s now time for the technical stuff!
So breathe deeply and get a beverage of your choice.
;-)
Here’s a list of all possible settings with their defaults.
You can fine-tune everything, but the defaults are great for
nearly all use cases!
# settings.autocue.cue_file.path := "cue_file"
# settings.autocue.cue_file.fade_in := 0.1 # seconds
# settings.autocue.cue_file.fade_out := 2.5 # seconds
# settings.autocue.cue_file.timeout := 60.0 # seconds
# settings.autocue.cue_file.target := -18.0 # LUFS
# settings.autocue.cue_file.silence := -42.0 # LU below track loudness
# settings.autocue.cue_file.overlay := -8.0 # LU below track loudness
# settings.autocue.cue_file.longtail := 15.0 # seconds
# settings.autocue.cue_file.overlay_longtail := -12.0 # extra LU
# settings.autocue.cue_file.sustained_loudness_drop := 40.0 # max. percent drop to be considered sustained
# settings.autocue.cue_file.noclip := false # clipping prevention like loudgain's `-k`
# settings.autocue.cue_file.blankskip := 0.0 # skip silence in tracks
# settings.autocue.cue_file.unify_loudness_correction := true # unify `replaygain_track_gain` & `liq_amplify`
# settings.autocue.cue_file.write_tags := false # write liq_* tags back to file
# settings.autocue.cue_file.write_replaygain := false # write ReplayGain tags back to file
# settings.autocue.cue_file.force_analysis := false # force re-analysis even if tags found
# settings.autocue.cue_file.nice := false # Linux/MacOS only: Use NI=18 for analysis
# settings.autocue.cue_file.use_json_metadata := true # pass metadata to `cue_file` as JSON
enable_autocue_metadata()
,
automatically setssettings.request.prefetch := 2
This means we will at all times have the next two requests available for immediate playout. It also gives autocue enough time to process requests in advance.
In AzuraCast, this blocks the first two entries in the “up next” queue from being deleteable.
You can use cue_file
on the commandline.
It returns standard JSON data:
$ cue_file "The_Vow_-_Spread_Some_Love.mp3"
Overlay: -14.72 LUFS, Longtail: -29.73 LUFS, Measured end avg: -30.91 LUFS, Drop: 38.45%
Overlay times: 177.30/180.10/0.00 s (normal/sustained/longtail), using: 180.10 s.
Cue out time: 181.10 s
{"duration": 181.2, "liq_cue_duration": 181.1, "liq_cue_in": 0.0, "liq_cue_out": 181.1, "liq_cross_start_next": 180.1, "liq_longtail": false, "liq_sustained_ending": true, "liq_loudness": "-6.72 LUFS", "liq_loudness_range": "5.86 LU", "liq_amplify": "-11.28 dB", "liq_amplify_adjustment": "0.00 dB", "liq_reference_loudness": "-18.00 LUFS", "liq_blankskip": 0.0, "liq_blank_skipped": false, "liq_true_peak": 1.177, "liq_true_peak_db": "1.42 dBFS"}
This is ideal for debugging or pre-processing scripts.
For sorted, more human-readable output, use jq -S
:
$ cue_file "The_Vow_-_Spread_Some_Love.mp3" | jq -S
Overlay: -14.72 LUFS, Longtail: -29.73 LUFS, Measured end avg: -30.91 LUFS, Drop: 38.45%
Overlay times: 177.30/180.10/0.00 s (normal/sustained/longtail), using: 180.10 s.
Cue out time: 181.10 s
{
"duration": 181.2,
"liq_amplify": "-11.28 dB",
"liq_amplify_adjustment": "0.00 dB",
"liq_blank_skipped": false,
"liq_blankskip": 0,
"liq_cross_start_next": 180.1,
"liq_cue_duration": 181.1,
"liq_cue_in": 0,
"liq_cue_out": 181.1,
"liq_longtail": false,
"liq_loudness": "-6.72 LUFS",
"liq_loudness_range": "5.86 LU",
"liq_reference_loudness": "-18.00 LUFS",
"liq_sustained_ending": true,
"liq_true_peak": 1.177,
"liq_true_peak_db": "1.42 dBFS"
}
Use cue_file --help
for more information.
$ cue_file --help
usage: cue_file [-h] [-V] [-t TARGET] [-s SILENCE] [-o OVERLAY] [-l LONGTAIL]
[-x EXTRA] [-d DROP] [-k] [-b [BLANKSKIP]] [-w] [-r] [-f] [-n]
[-j JSON]
file
Analyse audio file for cue-in, cue-out, overlay and EBU R128 loudness data,
results as JSON. Optionally writes tags to original audio file, avoiding
unnecessary re-analysis and getting results MUCH faster. This software is
mainly intended for use with my Liquidsoap "autocue:" protocol.
cue_file 4.0.2 supports writing tags to these file types:
.aac, .aif, .aifc, .aiff, .alac, .ape, .asf, .flac, .m2a, .m4a, .m4b, .m4p,
.m4r, .m4v, .mp+, .mp2, .mp3, .mp4, .mpc, .ofr, .ofs, .oga, .ogg, .ogv, .opus,
.spx, .wav, .wma, .wmv, .wv.
More file types are available when Mutagen is installed (True).
positional arguments:
file File to be processed
options:
-h, --help show this help message and exit
-V, --version show program's version number and exit
-t TARGET, --target TARGET
LUFS reference target; -23.0 to 0.0 (default: -18.0)
-s SILENCE, --silence SILENCE
LU below integrated track loudness for cue-in & cue-
out points (silence removal at beginning & end of a
track) (default: -42.0)
-o OVERLAY, --overlay OVERLAY
LU below integrated track loudness to trigger next
track (default: -8.0)
-l LONGTAIL, --longtail LONGTAIL
More than so many seconds of calculated overlay
duration are considered a long tail, and will force a
recalculation using --extra, thus keeping long song
endings intact (default: 15.0)
-x EXTRA, --extra EXTRA
Extra LU below overlay loudness to trigger next track
for songs with long tail (default: -15.0)
-d DROP, --drop DROP Max. percent loudness drop at the end to be still
considered having a sustained ending. Such tracks will
be recalculated using --extra, keeping the song ending
intact. Zero (0.0) to switch off. (default: 40.0)
-k, --noclip Clipping prevention: Lowers track gain if needed, to
avoid peaks going above -1 dBFS. Uses true peak values
of all audio channels. (default: False)
-b [BLANKSKIP], --blankskip [BLANKSKIP]
Skip blank (silence) within track if longer than
[BLANKSKIP] seconds (get rid of "hidden tracks"). Sets
the cue-out point to where the silence begins. Don't
use this with spoken or TTS-generated text, as it will
often cut the message short. Zero (0.0) to switch off.
Omitting [BLANKSKIP] defaults to 5.0 s. (default: 0.0)
-w, --write Write Liquidsoap liq_* tags to file. Ensure you have
enough free space to hold a copy of the original file.
(default: False)
-r, --replaygain Write ReplayGain tags to file (track only, no album).
Useful if your files have no previous RG tags. Only
valid if -w/--write is also specified. (default:
False)
-f, --force Force re-analysis, even if tags exist (default: False)
-n, --nice Linux/MacOS only: Use nice? Will run analysis at nice
level 18. (default: False)
-j JSON, --json JSON Read/override tags from a JSON file. Use - to read
from stdin. Intended for pre-processing software which
can, for instance, fill in values from their database
here. (default: None)
Note cue_file will use the LARGER value from the sustained ending and longtail
calculations to set the next track overlay point. This ensures special song
endings are always kept intact in transitions.
cue_file 4.0.2 knows about these tags:
duration, liq_amplify, liq_amplify_adjustment, liq_blank_skipped,
liq_blankskip, liq_cross_duration, liq_cross_start_next, liq_cue_duration,
liq_cue_in, liq_cue_out, liq_fade_in, liq_fade_out, liq_hook1_in,
liq_hook1_out, liq_hook2_in, liq_hook2_out, liq_hook3_in, liq_hook3_out,
liq_longtail, liq_loudness, liq_loudness_range, liq_ramp1, liq_ramp2,
liq_ramp3, liq_reference_loudness, liq_sustained_ending, liq_true_peak,
liq_true_peak_db, r128_track_gain, replaygain_reference_loudness,
replaygain_track_gain, replaygain_track_peak, replaygain_track_range.
The absolute minimum set to (possibly) avoid a re-analysis is:
duration, liq_cross_start_next, liq_cue_in, liq_cue_out,
replaygain_track_gain.
A full audio file analysis can take some time. cue_file tries to avoid a
(re-)analysis if all required data can be read from existing tags in the file.
Please report any issues to https://github.com/Moonbase59/autocue/issues
Metadata is used in a prioritized manner,
so parameters can easily be stored
and overridden if needed.
The priorities are, from low to high:
cue_file
cue_file
behaviour, and allow it to just use these values instead of
doing a costly re-analysis.
Nirvana song: 222 times faster!
Examples:
Tags written by cue_file -w
.
ReplayGain tags are used by cue_file
but only written back to audio files on request.
This preserves your data from unintended changes.
uri = "/home/matthias/Musik/Other/Jingles/Short"
jingles = playlist(prefix='annotate:liq_blankskip=0.0,'
^ 'liq_fade_in=0.10,liq_fade_out=0.10'
^ ':', uri)
A jingles playlist: We want to disable blank skipping and set fade-in and fade-out times to 0.1 s, respectively.
Values set here are used as annotations, which have the highest
priority. Just what we want.
Basically, we use three types of metadata:
liq_blankskip
(float)liq_cue_file
(bool)jingle_mode
(bool)songtype
(char)duration
(s)liq_amplify
(dB)liq_amplify_adjustment
(dB)liq_cross_start_next
(s)liq_cue_in
(s)liq_cue_out
(s)liq_reference_loudness
(LUFS)replaygain_track_gain
(dB)replaygain_reference_loudness
(LUFS)liq_blank_skipped
(bool)liq_cue_duration
(s)liq_longtail
(bool)liq_sustained_ending
(bool)liq_loudness
(LUFS)liq_loudness_range
(LU)liq_true_peak
(dBFS)There are a plethora of other metadata that are either used internally, or reserved for future expansion.
Fading data (duration, type, curve), cross duration, Opus Gain, ramp and hook points belong to this category.
Shows autocue information and results
Shows final values used in playout
(liq_*
& replaygain
metadata)
GitHub repo:
https://github.com/Moonbase59/autocue/
autocue2
. autocue.cue_file
is
the supported integrated solution for LS 2.2.5 & newer.
Matthias C. Hormann (“Moonbase59”)
If you like what you got, please consider to . Thank you! ❤️