autocue ⇧
If you like what you got, please consider to . Thank you! ❤️
On-the-fly JSON song cue-in, cue-out, overlay, replaygain calculation for Liquidsoap, AzuraCast and other AutoDJ software.
Check out the presentation for an introduction, the FAQ – Frequently Asked Questions, and the changelog for latest information!
Important
Please read IMPORTANT.md, an update on my personal situation and this project.
Warning
Autocue was made for Liquidsoap 2.2.5 and is not yet fully compatible with the new Liquidsoap 2.3.x rolling release, since some parameters have changed in Liquidsoap. I’ll start working on a new version when back.
Note: This documentation describes the standalone cue_file
and autocue.cue_file
, my integration into Liquidsoap’s autocue:
protocol.
Note: The previous integrate-with-liquidsoap
branch is now gone. I updated the master
branch and you can get the latest versions from that.
Requires Python3 and ffmpeg
with the ebur128 filter. Mutagen highly recommended. (The AzuraCast Docker already has these.)
Tested on Linux and Mac, with several ffmpeg
versions ranging from 4.4.2–6.1.1, and running on several stations since a few weeks.
Basically, autocue
consists of two parts:
cue_file
, a Python3 script, that returns JSON cueing, loudness and overlay data for an audio file. This can be used standalone, as part of some pre-processing or AutoDJ software, or in conjunction with below.- The Liquidsoap
autocue.cue_file
integration, for use with Liquidsoap’sautocue:
protocol, which in turn can be used standalone or as part of a larger playout system like AzuraCast or others.
Both standalone Liquidsoap operation and integrated playout systems like AzuraCast (and others) are supported. cue_file
, the central part of autocue.cue_file
, is available as CLI executable and can be used to integrate into other applications, for example to get the autocue
results into a database, or pre-tag your audio files.
Table of Contents ⇧
- autocue
- Table of Contents
- Install
- Command-line interface
- Parameters and settings reference
- Tags and metadata reference
- Examples
- Liquidsoap protocol
Install ⇧
Install cue_file
⇧
Put cue_file
in your path locally (i.e., into ~/bin
, ~/.local/bin
or /usr/local/bin
) and chmod +x
it (make executable).
If you wish, you can now play around with it a bit (use cue_file --help
for help), or follow our examples below and analyze some of your audio files. Audacity and spec can be helpful in checking.
Install Mutagen ⇧
Refer to the Mutagen Docs.
When installed, check cue_file --help
. It should tell you the file types it can handle, and if Mutagen is installed.
You can use cue_file --version
to find out the version of cue_file
and Mutagen, if it is installed.
$ cue_file --version
cue_file 4.0.3
mutagen 1.47.0
Local testing with Liquidsoap ⇧
Use the code in test_autocue.cue_file.liq
for some local testing, if you have Liquidsoap installed on your machine.
Adjust the settings near the beginning of the file, then look for
# --- Use YOUR (playlist/single) here! ---
and put in your song and jingle playlist, and possibly a single
for testing.
Then run
$ liquidsoap test_autocue.cue_file.liq
Depending on your settings, you’ll get some result files for further study:
test_autocue.cue_file.log
— Log file, usetail -f test_autocue.cue_file.log
in another terminal to followtest_autocue.cue_file.mp3
— an MP3 recording, to see how well the track transitions worked outtest_autocue.cue_file.cue
— a.cue
file to go with the MP3 recording, for finding tracks easier (open this in your audio player)
Install on AzuraCast ⇧
Since AzuraCast Rolling Release #caeea9d (2024-05-21), it is built-in!
In your station, just go to Profile → Edit Profile → AutoDJ → Audio Processing and enable it here:
I also recommend to disable Always Write Playlists to Liquidsoap under Advanced Configuration:
This will automatically install cue_file
, mutagen
and autocue.cue_file.liq
into AzuraCast, and change your Liquidsoap configuration as needed.
Settings ⇧
You can then add your personal settings in the second input box under Broadcasting → Edit Liquidsoap Configuration.
Here is a little hint: Just copy all possible settings into this input field, commented out, and just un-comment those you want to change. That’s a good way to never forget which settings are possible.
Here is a complete list you can copy, showing the default settings:
# 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
Then Save Changes and Restart Broadcasting.
Command-line interface ⇧
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.1.0 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: -12.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.1.0 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_file, 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
Parameters and settings reference ⇧
Here is a reference table for settings and parameters in cue_file
(the external executable) and autocue.cue_file
(the Liquidsoap integration):
cue_file | autocue.cue_file | Default | Note |
---|---|---|---|
-h , --help |
— | — | show help |
-V , --version |
— | — | show version |
-t , --target |
settings.autocue.cue_file.target |
-18.0 | LUFS |
-s , --silence |
settings.autocue.cue_file.silence |
-42.0 | LU |
-o , --overlay |
settings.autocue.cue_file.overlay |
-8.0 | LU |
-l , --longtail |
settings.autocue.cue_file.longtail |
15.0 | seconds |
-x , --extra |
settings.autocue.cue_file.overlay_longtail |
-12.0 | LU (0=disable) |
-d , --drop |
settings.autocue.cue_file.sustained_loudness_drop |
40.0 | % (0=disable) |
-k , --noclip |
settings.autocue.cue_file.noclip |
false | true/false |
-b , --blankskip |
settings.autocue.cue_file.blankskip |
0.0 | seconds (0=disable) |
-w , --write |
settings.autocue.cue_file.write_tags |
false | true/false |
-r , --replaygain |
settings.autocue.cue_file.write_replaygain |
false | true/false |
-f , --force |
settings.autocue.cue_file.force_analysis |
false | true/false |
-n , --nice |
settings.autocue.cue_file.nice |
false | true/false |
-j , --json |
— | None | file (- =stdin ) |
— | settings.autocue.cue_file.use_json_metadata |
true | true/false |
— | settings.autocue.cue_file.path |
cue_file | 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.unify_loudness_correction |
true | true/false |
— | settings.autocue.cue_file.ignored_overrides |
['duration'] | (list) |
— | settings.autocue.cue_file.version |
(version) | (SemVer) |
— | settings.autocue.cue_file.version_external |
(version) | (SemVer) |
Before changing any of these, please know exactly what you’re doing, and test locally before applying changes to your station!
Tags and metadata reference ⇧
Categories ⇧
Tags and metadata can be roughly grouped into categories:
Category | Short | |
---|---|---|
Settings | S | can be set in Autocue settings |
Controls | C | control Autocue behaviour and features |
Results from cue_file |
R | (JSON) |
Results from autocue.cue_file |
R | (Liquidsoap) |
Informational results | I | not used for computations, but useful to have |
Overrides | O | can be set in Visual Cue Editor/Advanced tab |
Reserved for future expansion | X | Planning ahead, so no one else accidentally uses these for something else |
Tags and metadata used by Autocue ⇧
For easier lookup, this table will be kept in alphabetical order. If in doubt, please also check the footnotes.
Name | Type | Data type | Stored as | Unit | Example |
---|---|---|---|---|---|
duration1 | R | float | float | s | 1235.121633 |
jingle_mode2 | C | bool | bool | — | true |
liq_amplify_adjustment | R,I | float | string | dB | 0.00 dB |
liq_amplify | R | float | string | dB | -7.53 dB |
liq_blank_skipped | R | bool | bool | — | true |
liq_blankskip | S,C | float | float | s | 5.00 |
liq_cross_duration3 | — | float | float | s | (do not use) |
liq_cross_start_next | R,O | float | float | s | 224.10 |
liq_cue_duration | R,I | float | float | s | 227.50 |
liq_cue_file | C | bool | bool | — | false |
liq_cue_in | R,O | float | float | s | 0.00 |
liq_cue_out | R,O | float | float | s | 227.50 |
liq_fade_in | O | float | float | s | 0.10 |
liq_fade_out | R,O | float | float | s | 2.50 |
liq_hook1_in | X,O4 | float | float | s | (reserved) |
liq_hook1_out | X,O4 | float | float | s | (reserved) |
liq_hook2_in | X | float | float | s | (reserved) |
liq_hook2_out | X | float | float | s | (reserved) |
liq_hook3_in | X | float | float | s | (reserved) |
liq_hook3_out | X | float | float | s | (reserved) |
liq_longtail | R,I | bool | bool | — | false |
liq_loudness_range | R,I | float | string | LU | 7.90 LU |
liq_loudness | R | float | string | LUFS | -10.47 LUFS |
liq_ramp1 | X,O4 | float | float | s | (reserved) |
liq_ramp2 | X | float | float | s | (reserved) |
liq_ramp3 | X | float | float | s | (reserved) |
liq_reference_loudness | S | float | string | LUFS | -18.00 LUFS |
liq_sustained_ending | R,I | bool | bool | — | false |
liq_true_peak_db | R,I | float | string | dBFS | 4.25 dBFS |
liq_true_peak | R,I | float | float | (linear) | 1.632000 |
r128_track_gain5 | R | int | int | — | -3359 |
replaygain_reference_loudness | S | float | string | LUFS | -18.00 LUFS |
replaygain_track_gain | R | float | string | dB | -7.53 dB |
replaygain_track_peak | R,I | float | float | (linear) | 1.632000 |
replaygain_track_range | R,I | float | string | LU | 7.90 LU |
songtype6 | C | char | string | — | S |
Tags and metadata format rules ⇧
If pre-tagging files manually, or modifying values in Liquidsoap, you must adhere to some rules:
- Float values typcially have a 2 decimals precision, peak values have 6 decimals.
- Do not use only a trailing period to indicate a float (like
0.
, as is often done in Liquidsoap). - The decimal point is always a period, never a decimal comma as in some languages like German.
- Integer values like
R128_TRACK_GAIN
must not be specified with a dot or any decimals. - Boolean values must be either
true
orfalse
, in all lowercase. We have no concept of "truthy" or "falsy" like some programming languages, soTrue
,TRUE
,yes
,1
or1.00
for example will not work astrue
. - Units should be written exactly as shown in above reference table, in the "Example" column. So do use a blank between value and unit, and use the exact name and casing shown, like
dB
, notdb
orDB
. This is not so much a requirement for Autocue andcue_file
, but more for other software, especially for the ReplayGain metadata. - Do not save
R128_TRACK_GAIN
to non-Opus files, and do not savereplaygain_*
tags to Opus files.cue_file
will take care of the necessary conversions, but you could have accidentally provided these in a tagging program, JSON or Liquidsoap. - Never save
duration
as a tag. The file duration is determined by other means (calculated or taken from the header info in various file types). Again cue_file takes care of it for its own calculations, but you could have set it elsewhere (tagging software, JSON).
Examples ⇧
Hidden track ⇧
The well-known Nirvana song Something in the Way / Endless, Nameless from their 1991 album Nevermind:
It contains the 3:48 song Something in the Way, followed by 10:03 of silence, followed by the "hidden track" Endless, Nameless.
Normal mode (no blank detection):
$ cue_file -f "Nirvana - Something in the Way _ Endless, Nameless.mp3"
Overlay: -18.47 LUFS, Longtail: -33.47 LUFS, Measured end avg: -41.05 LUFS, Drop: 38.91%
Overlay times: 1222.30/1228.10/0.00 s (normal/sustained/longtail), using: 1228.10 s.
Cue out time: 1232.20 s
{"duration": 1235.1, "liq_cue_duration": 1232.2, "liq_cue_in": 0.0, "liq_cue_out": 1232.2, "liq_cross_start_next": 1228.1, "liq_longtail": false, "liq_sustained_ending": true, "liq_loudness": "-10.47 LUFS", "liq_loudness_range": "7.90 LU", "liq_amplify": "-7.53 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.632, "liq_true_peak_db": "4.25 dBFS"}
With blank detection (cue-out at start of silence):
$ cue_file -fb -- "Nirvana - Something in the Way _ Endless, Nameless.mp3"
Overlay: -18.47 LUFS, Longtail: -33.47 LUFS, Measured end avg: -41.80 LUFS, Drop: 43.05%
Overlay times: 224.10/0.00/0.00 s (normal/sustained/longtail), using: 224.10 s.
Cue out time: 227.50 s
{"duration": 1235.1, "liq_cue_duration": 227.5, "liq_cue_in": 0.0, "liq_cue_out": 227.5, "liq_cross_start_next": 224.1, "liq_longtail": false, "liq_sustained_ending": false, "liq_loudness": "-10.47 LUFS", "liq_loudness_range": "7.90 LU", "liq_amplify": "-7.53 dB", "liq_amplify_adjustment": "0.00 dB", "liq_reference_loudness": "-18.00 LUFS", "liq_blankskip": 5.0, "liq_blank_skipped": true, "liq_true_peak": 1.632, "liq_true_peak_db": "4.25 dBFS"}
where
- duration — the real file duration (including silence at start/end of song), in seconds
- liq_cue_duration — the actual playout duration (cue-in to cue-out), in seconds
- liq_cue_in — cue-in point, in seconds
- liq_cue_out — cue-out point, in seconds
- liq_cross_start_next — suggested start point of next song, in seconds (counting from beginning of file)
- liq_longtail — flag to show if song has a "long tail", i.e. a very long fade-out (true/false)
- liq_sustained_ending — flag to show if song has a "sustained ending", i.e. not a "hard end" (true/false)
- liq_loudness — song’s EBU R128 integrated loudness, in LUFS
- liq_loudness_range — song’s loudness range (dynamics), in LU
- liq_amplify — "ReplayGain"-like value, offset to desired loudness target (i.e., -18 LUFS), in dB. This is intentionally not called replaygain_track_gain, since that tag might already exist and have been calculated using more exact tools like
loudgain
. - liq_amplify_adjustment — shows adjustment done by clipping prevention, in dB
- liq_reference_loudness — loudness reference target used, in LUFS, like replaygain_reference_loudness
- liq_blankskip — shows blank (silence) skipping used, in seconds (0.00=disabled)
- liq_blank_skipped — flag to show that we have an early cue-out, caused by silence in the song (true/false)
- liq_true_peak — linear true peak, much like replaygain_track_peak, but using a true peak algorithm
- liq_true_peak_db — true peak in dBFS (dBTP)
Long tail handling ⇧
Bohemian Rhapsody by Queen has a rather long ending, which we don’t want to destroy by overlaying the next song too early. This is where cue_file
’s automatic "long tail" handling comes into play. Let’s see how the end of the song looks like:
Here are the values we get from cue_file
:
$ cue_file -f "Queen - Bohemian Rhapsody.flac"
Overlay: -23.50 LUFS, Longtail: -38.50 LUFS, Measured end avg: -44.31 LUFS, Drop: 23.62%
Overlay times: 336.50/348.50/348.50 s (normal/sustained/longtail), using: 348.50 s.
Cue out time: 353.00 s
{"duration": 355.1, "liq_cue_duration": 353.0, "liq_cue_in": 0.0, "liq_cue_out": 353.0, "liq_cross_start_next": 348.5, "liq_longtail": true, "liq_sustained_ending": true, "liq_loudness": "-15.50 LUFS", "liq_loudness_range": "15.96 LU", "liq_amplify": "-2.50 dB", "liq_amplify_adjustment": "0.00 dB", "liq_reference_loudness": "-18.00 LUFS", "liq_blankskip": 0.0, "liq_blank_skipped": false, "liq_true_peak": 0.99, "liq_true_peak_db": "-0.09 dBFS"}
We notice the liq_longtail
flag is true
, and the "normal" overlay time would be 336.50
.
Let’s follow the steps cue_file
took to arrive at this result.
Cue-out point ⇧
cue_file
uses the -s
/--silence
parameter value (-42 LU default) to scan backwards from the end for something that is louder than -42 LU below the average (integrated) song loudness, using the EBU R128 momentary loudness algorithm. This is not a simple "level check"! Using the default (playout) reference loudness target of -18 LUFS
(-t
/--target
parameter), we thus arrive at a noise floor of -60 LU, which is a good silence level to use.
cue_file
has determined the cue-out point at 353.0
seconds (5:53).
Cross duration (where the next track could start and be overlaid) ⇧
cue_file
uses the -o
/--overlay
parameter value (-8 LU default) to scan backwards from the cue-out point for something that is louder than -8 LU below the average (integrated) song loudness, thus finding a good point where the next song could start and be overlaid.
cue_file
has determined a "normal" overlay start point (liq_cross_start_next
) of 336.5
seconds (5:36.5).
We can see this would destroy an important part of the song’s end.
A long tail! ⇧
Finding that the calculated cross duration of 16.5
seconds is longer than 15 seconds (the -l
/--longtail
parameter), cue_file
now recalculates the overlay start position automatically, using an extra -15 LU loudness offset (-x
/--extra
parameter, defaults to -12
in v4.0.3+), and arrives at this:
cue_file
has now set liq_cross_start_next
to 348.5
seconds and liq_longtail
to true
so we know this song has a "long tail" and been calculated differently.
Much better!
Avoiding too much overlap ⇧
We possibly don’t want the previous song to play "too much" into the next song, so we can set a default fade-out (settings.autocue.cue_file.fade_out
). This will ensure a pleasing limit. We use 2.5
seconds as a default:
settings.autocue.cue_file.fade_out := 2.5 # seconds
Fading area, using above settings. The rest of the ending won’t be heard.
Blank (silence) detection ⇧
Blank (silence) detection within a song is a great feature if you have songs with silence in the middle and a "hidden track" at the end. Autocue can then perform an early cue-out at the point where the silence begins. No one wants to broadcast 10 minutes of dead air, right?
This feature should be used wisely, because it could truncate tracks you wouldn’t like to end early, like jingles, ads, prerecorded shows, DJ sets or podcast episodes!
The default minimum silence length before it triggers is set to 5.0
seconds.
You can avoid issues in several ways:
- Don’t use the
-b
/--blankskip
option (default). - Set it to
0.00
, which disables the feature. - Increase the minimum silence length:
-b 10.0
for 10 seconds. - Manually assign later cue-in/cue-out points in the AzuraCast UI (user settings here overrule the automatic values).
Liquidsoap protocol ⇧
Note: autocue.cue_file
is meant to be used with Liquidsoap 2.2.5 or newer.
The protocol is invoked by prefixing a playlist or request with autocue:
like so:
radio = playlist(prefix="autocue:", "/home/matthias/Musik/Playlists/Radio/Classic Rock.m3u")
Alternatively, you can set enable_autocue_metadata()
and it will process all files Liquidsoap handles. Use either—or, but not both variants together. If running video streams, you might also want to exclude the video files from processing, by annotating liq_cue_file=false
for them, for instance as a playlist prefix. autocue
can handle multi-gigabyte video files, but that will eat up lots of CPU (and bandwidth) and might bring your station down.
autocue
offers the following settings (defaults shown):
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
Minimal working example ⇧
This minimal example enables autocue
for all tracks, using default settings, and plays a nicely crossfaded playlist to your sound card, so you can get a first impression. Just change the playlist to one of your own!
# minimal_example_autocue.cue_file.liq
# Minimal example for `autocue.cue_file`
# Uses one playlist and outputs to sound card.
%include "autocue.cue_file.liq"
# Your special non-default settings go here
# Check Autocue setup, print result, shutdown if problems
ignore(check_autocue_setup(shutdown=true, print=true))
enable_autocue_metadata()
# --- Use YOUR playlist here! ---
radio = playlist("/home/matthias/Musik/Playlists/Radio/Classic Rock.m3u")
# Use calculated `liq_amplify` for loudness correction
radio = amplify(1.,override="liq_amplify",radio)
# simplest crossfade possible, using `autocue.cue_file` calculated data
# and default settings
radio = crossfade(radio)
radio = mksafe(radio)
output(radio)
Next track and short jingle handling ⇧
With Liquidsoap 2.2.5+git@cadd05596 and newer:
If you have a long cross duration and a jingle following that is shorter than the computed crossing duration, Liquidsoap will now try to ensure the jingle still starts at the right position, and simply cut off the "overhang" from the previous track.
autocue
, if used, sets settings.request.prefetch := 2
to ensure there is always one more track ready. This is also new functionality. It helps "bridging the time" until autocue
has calculated data for the next track, which might take a while.
Tags/Annotations that influence autocue
’s behaviour ⇧
There are three possible annotations (or tags from a file) that can influence autocue
’s behaviour. In an annotation string, these must occur to the right of the protcol, i.e. autocue:annotate:...
to work as intended. Think of these as "controls" to enable or disable features.
liq_cue_file
(true
/false
/not set) ⇧
- not set — default behaviour (metadata can override
cue_file
results) false
— don’t autocue (still use metadata if present)true
—cue_file
results override metadata (special use cases)
You can disable autocueing for selected sources, like maybe a playlist of large video files, even when autocue
is globally enabled.
So if you’ve used
enable_autocue_metadata()
to globally enable autocue
, and want to exclude a playlist from processing, use:
p = playlist(prefix='annotate:liq_cue_file=false:', '/path/to/playlist.ext')
If a track has been skipped, it will be shown in the logs like this:
2024/04/01 10:47:00 [autocue.cue_file:2] Skipping cue_file for file "/home/matthias/Musik/Other/Jingles/Short/Magenta - How sentimental.flac" because liq_cue_file=false forbids it.
Note: Using this makes only sense if you used enable_autocue_metadata()
. When using the autocue:
protocol in your annotations, you’d simply leave the autocue:
part off the annotation instead.
For a more thorough explanation, see the FAQ
liq_blankskip
(seconds; 0.0 to disable) ⇧
You can override the "blankskip" behaviour (early cue-out of a song when silence is detected) on a per-request or per-playlist basis using a special liq_blankskip
annotation. This is an "ultimate override" which overrides both settings.autocue.cue_file.blankskip
and jingle_mode
.
For a playlist
, you could use its prefix
, like in
p = playlist(prefix='autocue:annotate:liq_blankskip=0.00:', '/path/to/playlist.ext')
For a single
, this would look like
s = single('autocue:annotate:liq_blankskip=0.00:/path/to/file.ext')
Or for a request
like
r = request.create('autocue:annotate:liq_blankskip=0.00:/path/to/file.ext')
This allows for a general protocol-wide setting, but exceptions for special content, like a playlist containing spoken content that would otherwise be cut.
The logs will show if blank skipping has been used on a track:
2024/06/17 21:18:53 [autocue.cue_file:3] Blank (silence) skipping active: true, set to 5. s
In the returned metadata, in liq_blank_skipped
, you’ll also receive information if something actually has been skipped. So for the Nirvana song above, it would show:
2024/06/17 21:25:14 [autocue.cue_file:3] ("liq_blank_skipped", "true")
liq_blankskip
is the control that controls autocue
’s behaviour, while liq_blank_skipped
is the result of the operation.
AzuraCast: jingle_mode
("true"
) ⇧
This is a convenience feature for AzuraCast users. If you set Hide Metadata from Listeners ("Jingle Mode") to ON for a playlist in AzuraCast, it will annotate requests for this playlist with jingle_mode="true"
. Even if blank skipping for songs is globally enabled, we would not want this to happen for jingles. They might contain pauses in speech that could cut them off early.
So if autocue
sees this annotation (or tag in a file), it will automatically disable "blankskip" for this track.
Note this setting is superceded by liq_blankskip
, the "ultimate blankskip control". So if both are there, the setting from liq_blankskip
will "win".
SAM Broadcaster Smart Categories ⇧
This is a convenience feature for those who came from SAM Broadcaster/SAM DJ, or share a music library with SAM, and have used SAM Broadcaster "Smart Categories" to categorize their tracks. Smart Categories are held in a songtype
tag, whose contents is a single letter: S
for normal songs, I
for Station ID, J
for Jingle, N
for News, and so on.
When detecting the songtype
tag in a file, and it is not S
(Song), autocue
will automatically disable "blankskip" for this track.
Note this setting is superceded by liq_blankskip
, the "ultimate blankskip control". So if both are there, the setting from liq_blankskip
will "win".
Effect of settings.autocue.cue_file.unify_loudness_correction
(true
/false
) ⇧
Unify replaygain_track_gain
and liq_amplify
. If enabled, this will ensure both have the same value, with replaygain_track_gain
taking precedence if we can see it. Allows scripts to amplify on either value, without getting loudness jumps.
Note: This can only work correctly if your files have been replaygained to the same LUFS target as your settings.autocue.cue_file.target
!
ReplayGain inserted ⇧
Here is an example of an inserted replaygain_track_gain
value (taken from the calculated liq_amplify
):
2024/06/17 18:23:11 [autocue.cue_file:3] Inserted replaygain_track_gain -8.36 dB and replaygain_reference_loudness -18.00 LUFS
ReplayGain overriding liq_amplify
⇧
Here liq_amplify
has been corrected, because we have seen a different replaygain_track_gain
(coming from a pre-tagged file where loudgain was used to ensure clipping prevention):
2024/04/02 08:54:48 [autocue.cue_file:3] Replaced liq_amplify=-8.72 dB with -9.71 dB from adjusted replaygain_track_gain
AzuraCast Notes ⇧
media:
URIs will be resolved.- Even when
settings.autocue.cue_file.blankskip := 5.0
, hidden jingles (those with ajingle_mode="true"
annotation) will be excluded from blank detection within the track, because the chance is too high that spoken text gets cut. - User settings in the AzuraCast UI ("Edit Song") always "win" over the calculated values.
- Currently generates lots of log data, for debugging. This will eventually change. But hey, you can see what it does!
Typical log sample (level 3; level 4 gives much more details):
2024/06/17 21:31:56 [autocue.cue_file:3] Now autocueing: "/var/azuracast/stations/niteradio/media/Tagged/Flogging Molly/Flogging Molly - Float (2008 album, DE)/Flogging Molly - Punch Drunk Grinning Soul.flac"
2024/06/17 21:31:56 [autocue.cue_file:3] Blank (silence) skipping active: true, set to 5. s
2024/06/17 21:31:56 [autocue.cue_file:3] Clipping prevention active: true
2024/06/17 21:31:56 [autocue.cue_file:3] Writing tags: false, including ReplayGain: false
2024/06/17 21:31:56 [autocue.cue_file:3] Writing metadata to /tmp/cue_file2fb6d2.json
...
2024/06/17 21:31:58 [autocue.cue_file:3] cue_file result for "/var/azuracast/stations/niteradio/media/Tagged/Flogging Molly/Flogging Molly - Float (2008 album, DE)/Flogging Molly - Punch Drunk Grinning Soul.flac": {"duration": 260.70000000000005, "liq_cue_duration": 260.5, "liq_cue_in": 0.0, "liq_cue_out": 260.5, "liq_cross_start_next": 259.9, "liq_longtail": true, "liq_sustained_ending": true, "liq_loudness": "-6.99 LUFS", "liq_loudness_range": "17.78 LU", "liq_amplify": "-11.02 dB", "liq_amplify_adjustment": "0.00 dB", "liq_reference_loudness": "-18.00 LUFS", "liq_blankskip": 5.0, "liq_blank_skipped": false, "liq_true_peak": 1.099, "liq_true_peak_db": "0.82 dBFS"}
2024/06/17 21:31:58 [autocue.cue_file:3] Clipping prevention: Adjusted calculated replaygain_track_gain from -11.02 dB to -11.01 dB
2024/06/17 21:31:58 [autocue.cue_file:3] No fade-in duration given, using default setting (0.1 s).
2024/06/17 21:31:58 [autocue.cue_file:3] No fade-out duration given, using default setting (2.5 s).
2024/06/17 21:31:58 [autocue.cue_file:2] Given fade-out duration (2.5 s) exceeds available time, using 0.6 s.
2024/06/17 21:31:58 [autocue.cue_file:3] Metadata added/corrected for "/var/azuracast/stations/niteradio/media/Tagged/Flogging Molly/Flogging Molly - Float (2008 album, DE)/Flogging Molly - Punch Drunk Grinning Soul.flac":
2024/06/17 21:31:58 [autocue.cue_file:3] ("duration", "260.70")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_amplify", "-11.01 dB")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_amplify_adjustment", "0.00 dB")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_blank_skipped", "false")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_blankskip", "5.")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_cross_start_next", "259.9")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_cue_duration", "260.50")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_cue_in", "0.")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_cue_out", "260.5")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_fade_in", "0.1")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_fade_out", "0.6")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_longtail", "true")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_loudness", "-6.99 LUFS")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_loudness_range", "17.78 LU")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_reference_loudness", "-18.00 LUFS")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_sustained_ending", "true")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_true_peak", "1.099")
2024/06/17 21:31:58 [autocue.cue_file:3] ("liq_true_peak_db", "0.82 dBFS")
2024/06/17 21:31:58 [autocue.cue_file:3] ("replaygain_reference_loudness", "-18.00 LUFS")
2024/06/17 21:31:58 [autocue.cue_file:3] ("replaygain_track_gain", "-11.01 dB")
Custom crossfading ⇧
I currently7 use these crossfade settings (third input box in AzuraCast; lots of debugging info here, could be much shorter).
Be sure to check the copy-paste sections in test_autocue.cue_file.liq
, which always holds the most current code.
# Fading/crossing/segueing
def live_aware_crossfade(old, new) =
if to_live() then
# If going to the live show, play a simple sequence
# fade out AutoDJ, do (almost) not fade in streamer
sequence([
fade.out(duration=settings.autocue.cue_file.fade_out(), old.source),
fade.in(duration=settings.autocue.cue_file.fade_in(), new.source)
])
else
# Otherwise, use a beautiful add
add(normalize=false, [
fade.in(
initial_metadata=new.metadata,
duration=settings.autocue.cue_file.fade_in(),
new.source
),
fade.out(
initial_metadata=old.metadata,
duration=settings.autocue.cue_file.fade_out(),
old.source
)
])
end
end
radio = cross(
duration=settings.autocue.cue_file.fade_out(),
live_aware_crossfade,
radio
)
With Liquidsoap 2.2.5, it’s important to pass the metadata to fade.in
and fade.out
. Otherwise, your autocue can’t function properly.
Using the autocue.cue_file
settings for the duration
parameter in fade.in
, fade.out
and cross
helps to always keep these settings in sync in case you change them: You now only have to change them in one place.
Footnotes
-
Note
duration
is not a tag, and shouldn’t be used as such! A file’s duration is determined by other means and that value returned asduration
metadata. ↩ -
The
jingle_mode
tag is used by AzuraCast to indicate if a track’s metadata should be suppressed. It is eithertrue
or non-existent. ↩ -
Liquidsoap internal, do not use! ↩
-
Note that
liq_hook1_in
,liq_hook1_out
andliq_ramp1
are not in AzuraCast yet, but I hope they’ll eventually be included. Having a measure for ramp talk/liners and being able to auto-generate hook sequence teasers would just be so nice! ↩ ↩2 ↩3 -
R128_TRACK_GAIN is typically used in Ogg Opus files, and always referenced to -23 LUFS. Opus files must not have
replaygain_*
tags. ↩ -
The tag
songtype
is used by SAM Broadcaster/SAM DJ to categorize tracks. ↩ -
As of 2024-06-17, using Liquidsoap 2.2.5. Liquidsoap has a very active development, so things might change. ↩