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 

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

Work in progress.

Note: This documentation describes the standalone cue_file and the autocue2 Liquidsoap protocol. Documentation for the integrated version in the integrate-with-liquidsoap branch will follow later.

Requires Python3 and ffmpeg with the ebur128 filter. (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 autocue2: protocol, for full integration into Liquidsoap, which in turn can be used standalone or as part of a larger playout system like AzuraCast or others.

Note: Liquidsoap recently introduced a bultin autocue: protocol. I had to rename my autocue: protocol to autocue2: so it doesn’t clash with the other one.

Both standalone Liquidsoap operation and integrated playout systems like AzuraCast (and others) are supported. cue_file, the central part of autocue2, is available as CLI executable and can be used to integrate into other applications, for example to get the autocue2 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.

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.

Local testing with Liquidsoap 

Use the code in test_autocue2.liq for some local testing, if you have Liquidsoap installed on your machine.

Adjust the settings near the beginning of the fie, then look for

# --- Use YOUR (playlist/single) here! ---

and put in your song and jingle playlist, and a single for testing.

Then run

$ liquidsoap test_autocue2.liq

Depending on your settings, you’ll get some result files for further study:

  • test_autocue2.log — Log file, use tail -f test_autocue2.liq in another terminal to follow
  • test_autocue2.mp3 — an MP3 recording, to see how well the track transitions worked out
  • test_autocue2.cue — a .cue file to go with the MP3 recording, for finding tracks easier (open this in your audio player)

Install on AzuraCast 

cue_file

On your AzuraCast host, you can put it into /var/azuracast/bin and overlay it into the Docker by adding a line to your docker-compose.override.yml, like so:

services:
  web:
    volumes:
      - /var/azuracast/bin/cue_file:/usr/local/bin/cue_file

Use Plugin or not? 

With AzuraCast, you now have two options for installing:

  1. Without modifying the generated AzuraCast Liquidsoap config. Use enable_autocue2_metadata() for that. Drawback: You can’t use settings.autocue2.blankskip := true and expect "hidden" jingles to be automatically exempted from finding silent parts in the tracks.
  2. You can modify the AzuraCast Liquidsoap config by installing @RM-FM’s ls-config-replace plugin, and copying over the ls-config-replace/liq/10_audodj_next_song_add_autocue folder into your /var/azuracast/plugins/ls-config-replace/liq folder after installing the plugin. This will allow using all features.

If you wish to disable AzuraCast’s built-in liq_amplify handling and rather use your tagged ReplayGain data, also copy over the 12_remove_amplify folder.

Add the autocue2 protocol code 

Then copy-paste the contents of the autocue2.liq file into the second input box in your station’s Liquidsoap Config.

If you want to enable skipping silence within tracks, add the following line at the end of this input box:

settings.autocue2.blankskip := true

Note: This should only be used with installation variant 2 (plugin/modify config).

For installation variant 1 (enable_autocue2_metadata()), this command also goes here, second input box, just after the settings:

enable_autocue2_metadata()

Custom crossfading code 

Use the example in test_autocue2.liq and add your custom amplify and live_aware_crossfade code in the third input box of AzuraCast’s Liquidsoap Config. You might already have modified this in your local testing above.

To find the relevant parts, look out for

# --- Copy-paste ...

Note: If you had used ReplayGain adjustment before like so (third input box)

# Be sure to have ReplayGain applied before crossing.
radio = amplify(1.,override="replaygain_track_gain",radio)

you might want to delete or comment out these lines. The autocue2: protocol already calculates a liq_amplify value that is recognized by AzuraCast and roughly equals a ReplayGain "track gain". If you would leave both in, you’d get a much too quiet playout.

ReplayGain vs. liq_amplify

If you have disabled AzuraCast’s built-in liq_amplify handler (by copying 12_remove_amplify above), you have the choice. But you must define your own adjustment (start of third input box).

Example, to use your own tagged ReplayGain Track Gain instead:

# Be sure to have ReplayGain or "liq_amplify" applied before crossing.
radio = amplify(1.,override="replaygain_track_gain",radio)
#radio = amplify(1.,override="liq_amplify",radio)

Pros & Cons:

  • ReplayGain can be more exact (and prevent clipping) if pre-tagged with a tool like loudgain.
  • ReplayGain values should exist as tags in your audio files. If not, and settings.autocue2.unify_loudness_correction := true, autocue2 will insert an (internal) replaygain_track_gain value.
  • cue_file (and thus the autocue2: protocol) will always calculate a liq_amplify value on the fly, so it can be used with any audio file (even when not pre-tagged)
  • liq_amplify has no means of clipping prevention or EBU-recommended -1 dB/LU margin. The value still resembles a ReplayGain Track Gain closely, in most cases.
  • Both can be used with autocue2:.

Save and Restart Broadcasting.

Command-line interface 

usage: cue_file [-h]
                [-t {-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0}]
                [-s SILENCE] [-o OVERLAY] [-l LONGTAIL] [-x EXTRA] [-b] [-w]
                [-f]
                file

Return cue-in, cue-out, overlay and replaygain data for an audio file as JSON.
To be used with my Liquidsoap "autocue:" protocol.

positional arguments:
  file                  File to be processed

options:
  -h, --help            show this help message and exit
  -t {-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0}, --target {-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0}
                        LUFS reference target (default: -18.0)
  -s SILENCE, --silence SILENCE
                        LU/dB 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/dB 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/dB below overlay loudness to trigger next
                        track for songs with long tail (default: -15.0)
  -b, --blankskip       Skip blank (silence) within song (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.
                        (default: False)
  -w, --write           Write Liquidsoap liq_* tags to file. Use with care, as
                        ffmpeg can't write all tags to all file types! Ensure
                        you have enough free space to hold a copy of the
                        original file. (default: False)
  -f, --force           Force re-calculation, even if tags exist (default:
                        False)

Please report any issues to https://github.com/Moonbase59/autocue/issues

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 "Nirvana - Something in the Way _ Endless, Nameless.mp3"
{"duration": 1235.1, "liq_cue_duration": 1231.8, "liq_cue_in": 0.4, "liq_cue_out": 1232.2, "liq_cross_start_next": 1222.3, "liq_longtail": false, "liq_cross_duration": 9.900000000000091, "liq_loudness": "-10.47 dB", "liq_amplify": "-7.53 dB", "liq_blank_skipped": false}

With blank detection (cue-out at start of silence):

$ cue_file -b "Nirvana - Something in the Way _ Endless, Nameless.mp3"
{"duration": 1235.1, "liq_cue_duration": 227.1, "liq_cue_in": 0.4, "liq_cue_out": 227.5, "liq_cross_start_next": 224.1, "liq_longtail": false, "liq_cross_duration": 3.4000000000000057, "liq_loudness": "-10.47 dB", "liq_amplify": "-7.53 dB", "liq_blank_skipped": true}

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_cross_duration — suggested crossing duration for next song, in seconds backwards from cue-out point
  • liq_loudness — song’s EBU R128 loudness, in dB (=LU)
  • liq_amplify — simple "ReplayGain" value, offset to desired loudness target (i.e., -18 LUFS). 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_blank_skipped — flag to show that we have an early cue-out, caused by silence in the song (true/false)

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 "Queen - Bohemian Rhapsody.flac"
{"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_cross_duration": 4.5, "liq_loudness": "-15.50 dB", "liq_amplify": "-2.50 dB", "liq_blank_skipped": false}

We notice the liq_longtail flag is true, and the liq_cross_duration is 4.5 seconds.

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) 

Liquidsoap uses a liq_cross_duration concept instead of an abolute "start next" point, since that could be ambiguous (start counting at the beginning of the file or at the cue-in point?). The cross duration is measured in seconds backwards from the cue-out point.

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 cross duration of 16.5 seconds, starting at 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 cross duration automatically, using an extra -15 LU loudness offset (-x/--extra parameter), 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_duration to 4.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 in our custom crossfading. This will ensure a limit in case no user-defined fade-out has been set. We use 2.5 seconds in the example below (under AzuraCast Notes, in live_aware_crossfade):

add(normalize=false, [fade.in(duration=.1, delay=delay, new.source), fade.out(duration=2.5, delay=delay, old.source)])

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 

Note: Blank detection within a song is experimental. It will most certainly fail on spoken or TTS-generated messages (since spoken text has long pauses), and it can fail on some songs with very smooth beginnings, like Sarah McLachlan’s Fallen:

Screenshot of Sarah McLachlan's "Fallen" waveform. The song has a long silent beginning, which makes blank detection fail, using the default settings.

This song works fine in "normal mode", but only a 0.5 second portion (marked) of the beginning is played in "blank detection" mode when using the default settings:

$ cue_file -b "McLachlan, Sarah - Fallen (radio mix).flac"
{"duration": 229.0, "liq_cue_duration": 0.5, "liq_cue_in": 2.3, "liq_cue_out": 2.8, "liq_cross_start_next": 2.8, "liq_longtail": false, "liq_cross_duration": 0.0, "liq_loudness": "-8.97 dB", "liq_amplify": "-9.03 dB", "liq_blank_skipped": true}

You can avoid such issues in several ways:

  • Don’t use the -b/--blankstrip option (default).
  • Lower the silence level: -s -50/--silence -50.
  • Manually assign later cue-in/cue-out points in the AzuraCast UI (user settings here overrule the automatic values).

Example result when reducing the silence level to -50 LU below average:

$ cue_file -b -s -50 "McLachlan, Sarah - Fallen (radio mix).flac"
{"duration": 229.0, "liq_cue_duration": 221.79999999999998, "liq_cue_in": 1.9, "liq_cue_out": 223.7, "liq_cross_start_next": 215.6, "liq_longtail": false, "liq_cross_duration": 8.099999999999994, "liq_loudness": "-8.97 dB", "liq_amplify": "-9.03 dB", "liq_blank_skipped": false}

Notice the new cue-in and cue-out times as well as the long cross duration with this change! Additionally, liq_blank_skipped is false, showing us that no silent (blank) parts have been skipped in this case.

Liquidsoap protocol 

Note: The autocue2: protocol is meant to be used with Liquidsoap 2.2.5 or newer.

The protocol is invoked by prefixing a playlist or request with autocue2: like so:

radio = playlist(prefix="autocue2:", "/home/matthias/Musik/Playlists/Radio/Classic Rock.m3u")

Alternatively, you can set enable_autocue2_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_autocue2=false for them, for instance as a playlist prefix. autocue2 can handle multi-gigabyte video files, but that will eat up lots of CPU (and bandwidth) and might bring your station down.

autocue2 offers the following settings (defaults shown):

settings.autocue2.path := "cue_file"
settings.autocue2.timeout := 60.
settings.autocue2.target := -18
settings.autocue2.silence := -42
settings.autocue2.overlay := -8
settings.autocue2.longtail := 15.0
settings.autocue2.overlay_longtail := -15
# The following can be overridden by the `liq_blankskip` annotation
# on a per-request or per-playlist basis
settings.autocue2.blankskip := false
# Unify can only work correctly if your files have been replaygained
# to the same LUFS target as your `settings.autocue2.target`,
# usually -23 or -18 LUFS (-18 corresponds to the now obsolete `mp3gain` value "89 dB").
settings.autocue2.unify_loudness_correction := true

Minimal working example 

This minimal example enables autocue2 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_autocue2.liq
# 2024-04-09 - Moonbase59
# 2024-04-19 - Moonbase59 - rename to "minimal_example_autocue2.liq"

# Minimal example for the `autocue2` protocol.
# Uses one playlist and outputs to sound card.

%include "autocue2.liq"
enable_autocue2_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 `autocue2` calculated data
# set fades to your preference
radio = crossfade(radio, fade_in=0.1, fade_out=2.5)

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 liq_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.

autocue2, 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 autocue2 has calculated data for the next track, which might take a while.

Tags/Annotations that influence autocue2’s behaviour 

There are three possible annotations (or tags from a file) that can influence autocue2’s behaviour. In an annotation string, these must occur to the right of the protcol, i.e. autocue2:annotate:... to work as intended. Think of these as "switches" to enable or disable features.

liq_autocue2 (true/false) 

Note: I had to rename my original liq_autocue to liq_autocue2, because Liquidsoap now also uses liq_autocue, for another purpose. Sigh.

You can disable autocueing for selected sources, like maybe a playlist of large video files, even when autocue2 is globally enabled.

So if you’ve used

enable_autocue2_metadata()

to globally enable autocue2, and want to exclude a playlist from processing, use:

p = playlist(prefix='annotate:liq_autocue2="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 [autocue2_metadata:2] Skipping autocue2 for file "/home/matthias/Musik/Other/Jingles/Short/Magenta - How sentimental.flac" because liq_autocue2=false forbids it.

Note: Using this makes only sense if you used enable_autocue2_metadata(). When using the autocue2: protocol in your annotations, you’d simply leave the autocue2: part off the annotation instead.

liq_blankskip (true/false) 

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.autocue2.blankskip and jingle_mode.

For a playlist, you could use its prefix, like in

p = playlist(prefix='autocue2:annotate:liq_blankskip="false":', '/path/to/playlist.ext')

For a single, this would look like

s = single('autocue2:annotate:liq_blankskip="false":/path/to/file.ext')

Or for a request like

r = request.create('autocue2:annotate:liq_blankskip="false":/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/04/01 06:43:40 [autocue2.compute:3] Blank (silence) skipping active: true

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/04/01 11:00:47 [autocue2.metadata:3] ("liq_blank_skipped", "true")

liq_blankskip is the switch that controls autocue2’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 autocue2 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 switch". So if both are there, the setting from liq_blankskip will "win".

Effect of settings.autocue2.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.autocue2.target!

ReplayGain inserted 

Here is an example of an inserted replaygain_track_gain value (taken from the calculated liq_amplify):

2024/04/02 09:07:57 [autocue2.metadata:3] Inserted replaygain_track_gain: -8.36 dB

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 [autocue2.metadata:3] Replaced liq_amplify=-8.72 dB with -9.71 dB from replaygain_track_gain

AzuraCast Notes 

  • media: URIs will be resolved.
  • Works well with smart crossfades. (But these are definitely not needed, see code below!)
  • Even when settings.autocue2.blankskip := true, 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 needs a patch to the AzuraCast-generated Liquidsoap code to enable all features, but you can also opt to use enable_autocue2_metadata() instead, not use settings.autocue2.blankskip := true and skip all the plugin-related things.
  • 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/04/13 14:24:12 [autocue2.metadata:3] jingle_mode=, liq_blankskip=
2024/04/13 14:24:12 [autocue2:3] Now autocueing: "/home/matthias/Musik/Playlists/Radio/../../Tagged/Blackfoot/Blackfoot - Marauder/Blackfoot - Diary Of A Workingman.mp3"
2024/04/13 14:24:12 [autocue2:3] Blank (silence) skipping active: true

...

2024/04/13 14:24:16 [autocue2:3] Autocue2 result for "/home/matthias/Musik/Playlists/Radio/../../Tagged/Blackfoot/Blackfoot - Marauder/Blackfoot - Diary Of A Workingman.mp3": {"duration": 336.40000000000003, "liq_cue_duration": 333.8, "liq_cue_in": 0.7, "liq_cue_out": 334.5, "liq_cross_start_next": 323.5, "liq_longtail": false, "liq_cross_duration": 11.0, "liq_loudness": "-13.35 dB", "liq_amplify": "-4.65 dB", "liq_blank_skipped": false}
2024/04/13 14:24:16 [autocue2.metadata:3] Replaced liq_amplify=-4.65 dB with -4.66 dB from replaygain_track_gain
2024/04/13 14:24:16 [autocue2.metadata:3] Metadata added/corrected:
2024/04/13 14:24:16 [autocue2.metadata:3] ("duration", "336.4")
2024/04/13 14:24:16 [autocue2.metadata:3] ("liq_amplify", "-4.66 dB")
2024/04/13 14:24:16 [autocue2.metadata:3] ("liq_blank_skipped", "false")
2024/04/13 14:24:16 [autocue2.metadata:3] ("liq_cross_duration", "11.")
2024/04/13 14:24:16 [autocue2.metadata:3] ("liq_cross_start_next", "323.5")
2024/04/13 14:24:16 [autocue2.metadata:3] ("liq_cue_duration", "333.80")
2024/04/13 14:24:16 [autocue2.metadata:3] ("liq_cue_in", "0.7")
2024/04/13 14:24:16 [autocue2.metadata:3] ("liq_cue_out", "334.5")
2024/04/13 14:24:16 [autocue2.metadata:3] ("liq_longtail", "false")
2024/04/13 14:24:16 [autocue2.metadata:3] ("liq_loudness", "-13.35 dB")
2024/04/13 14:24:16 [autocue2.metadata:3] ("replaygain_track_gain", "-4.66 dB")

Custom crossfading 

I currently1 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_autocue2.liq, which always holds the most current code.

# Fading/crossing/segueing
def live_aware_crossfade(old, new) =
    label = "live_aware_crossfade"
    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=2.5,old.source),fade.in(duration=0.1,new.source)])
    else
        # Otherwise, use the simple transition
        log.important(label=label, "Using custom crossfade")
        if (old.metadata["jingle_mode"] == "true")
          and (new.metadata["jingle_mode"] == "true") then
            log.important(label=label, "Jingle → Jingle transition")
        end
        if (old.metadata["jingle_mode"] == "true")
          and (new.metadata["jingle_mode"] == "") then
            log.important(label=label, "Jingle → Song transition")
        end
        if (old.metadata["jingle_mode"] == "")
          and (new.metadata["jingle_mode"] == "true") then
            log.important(label=label, "Song → Jingle transition")
        end
        if (old.metadata["jingle_mode"] == "")
          and (new.metadata["jingle_mode"] == "") then
            log.important(label=label, "Song → Song transition")
        end

        nd = float_of_string(default=0.1, list.assoc(default="0.1", "liq_cue_duration", new.metadata))
        xd = float_of_string(default=0.1, list.assoc(default="0.1", "liq_cross_duration", old.metadata))
        delay = max(0., xd - nd)
        log.important(label=label, "Cross/new/delay: #{xd} / #{nd} / #{delay} s")
        if (xd > nd) then
          log.severe(label=label, "Cross duration #{xd} s longer than next track (#{nd} s)!")
          #log.severe(label=label, "Delaying fade-out & next track fade-in by #{delay} s.")
        end

        # Starting with LS 2.2.5+git@cadd05596, we don’t need the delay anymore
        add(normalize=false, [fade.in(initial_metadata=new.metadata, duration=.1, new.source), fade.out(initial_metadata=old.metadata, duration=2.5, old.source)])

        #cross.simple(old.source, new.source, fade_in=0.1, fade_out=2.5)
        #cross.smart(old, new, fade_in=0.1, fade_out=2.5, margin=8.)
    end
end

radio = cross(duration=3.0, width=2.0, live_aware_crossfade, radio)

The 2.5 s fade-out helps tuning long overlap durations down, so they won’t distract the listener by overlaying songs and possibly jingles too long. If using cross.smart, the increased margin (8 dB/LU) helps making the smart crossfades sound much better.

Footnotes

  1. As of 2024-04-07, using Liquidsoap 2.2.5+git@317f191c0. Liquidsoap has a very active development, so things might change.

About

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

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages