Skip to content

On-the-fly JSON song cue-in, cue-out, overlay, replaygain calculation for Liquidsoap, AzuraCast and other AutoDJ software.

License

Notifications You must be signed in to change notification settings

Moonbase59/autocue

Repository files navigation

autocue 

If you like what you got, please consider to Donate with Paypal. 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’s autocue: 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 

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.

cue_file supported file types

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, use tail -f test_autocue.cue_file.log in another terminal to follow
  • test_autocue.cue_file.mp3 — an MP3 recording, to see how well the track transitions worked out
  • test_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:

Screenshot of AzuraCast setting Enable Autocue; enabled.

I also recommend to disable Always Write Playlists to Liquidsoap under Advanced Configuration:

Screenshot of AzuraCast setting Always Write Playlists to Liquidsoap; disabled.

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 or false, in all lowercase. We have no concept of "truthy" or "falsy" like some programming languages, so True, TRUE, yes, 1 or 1.00 for example will not work as true.
  • 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, not db or DB. This is not so much a requirement for Autocue and cue_file, but more for other software, especially for the ReplayGain metadata.
  • Do not save R128_TRACK_GAIN to non-Opus files, and do not save replaygain_* 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:

Screenshot of Nirvana song waveform, showing a 10-minute silent gap in the middle

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:

Screenshot of Queen's Bohemian Rhapsody waveform, showing the almost 40 second long silent ending

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.

Screenshot of Bohemian Rhapsody waveform, showing calculated cue-out point at 353.0 seconds (2 seconds before end)

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.

Screenshot of Bohemian Rhapsody waveform, showing the cross duration calculated in the first run: 16.5 seconds before end – way too much

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:

Screenshot of Bohemian Rhapsody waveform, showing the newly calculated cross duration: 4.5 seconds before end – just right, not cutting off important parts of the song ending

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

Screenshot of Bohemian Rhapsody waveform, showing the user-defined fade-out of 2.5 seconds, starting 4.5 seconds before song ends

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 eitheror, 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)
  • truecue_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 a jingle_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

  1. 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 as duration metadata.

  2. The jingle_mode tag is used by AzuraCast to indicate if a track’s metadata should be suppressed. It is either true or non-existent.

  3. Liquidsoap internal, do not use!

  4. Note that liq_hook1_in, liq_hook1_out and liq_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

  5. R128_TRACK_GAIN is typically used in Ogg Opus files, and always referenced to -23 LUFS. Opus files must not have replaygain_* tags.

  6. The tag songtype is used by SAM Broadcaster/SAM DJ to categorize tracks.

  7. As of 2024-06-17, using Liquidsoap 2.2.5. Liquidsoap has a very active development, so things might change.