Recently, I wanted to generate a single audio file containing the mix of several others, in order to publish it as a podcast.
The purpose of this is, based on an playlist of songs, to generate a single audio file containing an enhanced sequence of these songs. We would also like to normalize the volumes between the songs and have nice crossfaded transitions.
As you can imagine, I used my tool of choice for that purpose: liquidsoap !
In the following is presented the script that performs that. It takes a playlist, located at ./playlist.m3u, and introductory jingle, located at ./intro.mp3 and generates mix.mp3, mix.ogg and mix.aac.
The script is commented. There were minor issues with the playlist parsing not preserving the ordering, so it should be run with a recent SVN code.
Also, the script uses a perl handler to extract replay gain information. This script is:
#!/usr/bin/perl -w
# A perl script that get the replay gain
# from either a vorbis or an mp3 file
# and returns it.
use strict ;
my $file = $ARGV[0] || die ;
if ($file =~ /\.mp3$/i) {
my $out = `nice -n 20 mp3gain "$file" 2> /dev/null` ;
$out =~ /Recommended "Track" dB change: (.*)$/m || die ;
print "$1 dB\n" ;
} elsif ($file =~ /\.ogg$/i) {
system("nice -n 20 vorbisgain -f \"$file\" \
2>/dev/null >/dev/null") ;
my $info = `ogginfo "$file"` ;
$info =~ /REPLAYGAIN_TRACK_GAIN=(.*) dB/ || die ;
print "$1 dB\n" ;
}Eventually, the final script is:
# Liquidsoap script that generates the mix of a playlist
# in a single audio file, with normalization using
# replay gain, and smart crossfading.
# Author: Romain Beauxis <toots@rastageeks.org>
# License: DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
# License URL: http://sam.zoy.org/wtfpl/COPYING
########### Settings ###########
# In this section, we declare
# the settings for this script.
# Log to stdout
set("log.file",false)
set("log.stdout",true)
# Uncomment to allow verbose logging..
#set("log.level",5)
# Disable real-time processing, to process with the maximun speed
set("root.sync",false)
########### Replay Gain Backend ###########
# In this section, we register the replay
# gain backend, to add replay gain information
# to each resolved file.
# Function to add replay_gain information
# to the metadata
def replaygain_metadata(~format,file)
x = get_process_lines("./replaygain.pl #{quote(file)}")
if list.hd(x) != "" then
[("replay_gain",list.hd(x))]
else
[]
end
end
# We register the above function as a new metadata resolver.
add_metadata_resolver("replay_gain",replaygain_metadata)
########### Source Declaration ###########
# In this section, we declare the main source
# that we want to output.
# An introductory single
intro = single("./intro.mp3")
# The main playlist
playlist = playlist.once("./playlist.m3u")
# We normalize audio files, according to the
# replay_gain information (see above).
final = amplify(1.,override="replay_gain",sequence([intro,playlist]))
# We use the smart crossfade, to crossfade between tracks,
# paying attention to the relative volumes.
# It is of course advised to place this operator *after*
# the normalization !
final = smart_crossfade(final)
########### Shutdown fallback ###########
# In this section, we wrap the main source
# in order to shutdown liquidsoap when
# it has finished to play.
# We define a special transition, that
# triggers the shutdown..
def end_trans(a,b) =
shutdown ()
b
end
# The default transition
def_trans = fun (a,b) -> b
# The fallback source: play the whole mix, and
# then fallback to a blank. The end_trans transition
# makes sure that liquidsoap stops when falling back to the blank
# source, i.e. at the end of the mixed playlist.
final = fallback(transitions=[def_trans,end_trans],
track_sensitive=false, [final,blank()])
########### Outputs ###########
# In this section, we define the output
# that we want.
# Output in mp3 format
output.file.mp3("mix.mp3",final)
# Output in ogg/vorbis format
output.file.vorbis("mix.ogg",final)
# Output in AAC+ format in 64kbps
# (needs aacplusenc in the path)
output.pipe.external(process=fun (_) -> "aacplusenc - mix.aac 64",
final)
blog.rastageeks.org