Creating an Internet Radio Station with Icecast and Liquidsoap

Bill Dengler

Issue #280, August 2017

Ever wanted to stream prerecorded music or a live event, such as a lecture or concert for an internet audience? With Icecast and Liquidsoap, you can set up a full-featured, flexible internet radio station using free software and open standards.

Icecast is “a streaming media (audio/video) server that currently supports Ogg (Vorbis and Theora), Opus, WebM and MP3 streams. It can be used to create an internet radio station or a privately running jukebox and many things in between. It is very versatile in that new formats can be added relatively easily and supports open standards for communication and interaction.”

Liquidsoap is “a powerful and flexible language for describing your streams. It offers a rich collection of operators that you can combine at will, giving you more power than you need for creating or transforming streams. But Liquidsoap is still very light and easy to use, in the UNIX tradition of simple strong components working together.”

When combined, Icecast and Liquidsoap can create a flexible, feature-rich internet radio station. In this article, I describe how to configure Icecast to host an internet radio station. Then, I explain how to install and configure Liquidsoap to connect to Icecast, adding random (or sequential) music playback with smart cross-fading, prerecorded randomly inserted announcements and jingles, a song request system and support for live streams, with automated recording and seamless switching between live and automated programming. I also show how to configure the server to serve your stream in MP3, Ogg and Opus formats for maximum player compatibility.

Icecast, Vorbis and related projects are maintained by Xiph.Org (https://www.xiph.org), a nonprofit organization that develops open multimedia standards and software. To ensure that you are running the latest version of Icecast, with all (or most) features, you should install from an official Xiph.Org repository. Visit the list of official repositories at https://wiki.xiph.org/Icecast_Server/Installing_latest_version_(official_Xiph_repositories), and follow the instructions on that page to add the Icecast repository for your distribution. Then, install using your system's package manager. On Debian-based systems (such as Ubuntu), you may be asked to “configure Icecast” during package installation; select “no” as you will configure the server manually if you are following along with this article.

Open the Icecast configuration file using your preferred text editor. On Debian-based systems, the file is located at /etc/icecast2/icecast.xml. The location on other systems may differ; check your package's documentation for the correct path. The configuration file is in XML format and is divided into several sections. First, enter your server's location and email into the location and admin fields, respectively—for example:


<location>The Heart of Gold</location>
<admin>zaphodb42@mail.example.com</admin>

Since each format you'll set up in Liquidsoap is a separate Icecast “source”, you'll quickly exhaust the default source limit of two. So, change that to ten:


<sources>10</sources>

Unless you anticipate listeners connecting from slow or low-bandwidth environments, disabling Icecast's burst-on-connect feature will significantly decrease latency:


<burst-on-connect>0</burst-on-connect>
        <burst-size>0</burst-size>

The default passwords, “hackme”, invite security compromise. Change them to something else. Also, it's probably a good idea to change the default admin user name. The following passwords are just examples; change them for your configuration both here and when they are mentioned later in the article:


<source-password>dontpanic</source-password>
    <relay-password>dontpanic42</relay-password>
            <admin-user>zaphod</admin-user>
            <admin-password>2Headsarebetterthanone!</admin-password>

Enter your system's fully qualified domain name in the hostname field:


<hostname>example.com</hostname>

Save and close the file. If you edited the file as root, you'll need to reset its permissions. On Debian-based systems, Icecast runs under user icecast2 and group icecast. To fix permissions on a Debian-based system, run:

chown icecast2:icecast /etc/icecast2/icecast.xml

On Debian-based systems, Icecast's system service is disabled by default. Open the file /etc/default/icecast2, and set enabled to true. Then save and close the file.

Most modern Linux systems use systemd for service management. To enable Icecast on boot and start it for this session, run the following commands as root (using sudo or similar):

systemctl enable icecast2
systemctl start icecast2

Service names on various systems differ; if those commands don't work, check your system's documentation for the correct service name.

Many distributions provide broken and out-of-date versions of Liquidsoap in their repositories. For this reason (along with improved ability to customize your installation), the Liquidsoap developers recommend installing it using the OCaml Package Manager (opam). Use your distro's package manager to install opam. If you've been doing everything up to this point logged in as root, you'll now need to create a non-root user under which to install Liquidsoap. You also need to install sudo and give this new user permission to use it. On Debian-based systems, the adduser and gpasswd utilities allow you to create users and add them to groups, respectively. On Debian-based systems, run the following commands as root to add a new user and grant it sudo access (for other systems, refer to the documentation). Let username represent the user name of the new user:

adduser username
gpasswd -a username sudo

Performing as your non-root user, initialize the OCaml Package Manager by running:

opam init

Answer “yes” when asked to modify your profile; this will place Liquidsoap on your path and allow it to be executed when you type its name. To apply opam changes, run:

eval `opam config env`

Next, install Liquidsoap's system dependencies:

opam install depext
opam depext taglib mad lame vorbis cry ssl samplerate 
 ↪magic opus liquidsoap

Now, install liquidsoap by replacing depext with install:

opam install taglib mad lame vorbis cry ssl samplerate 
 ↪magic opus liquidsoap

To set up a starting point for the station configuration and enable Liquidsoap as a service, the developers have created liquidsoap-daemon, a set of scripts for using Liquidsoap as a system service. Liquidsoap-daemon uses systemd for service management by default; therefore, it is compatible with most modern Linux distributions. To set it up, install Git using your system's package manager, then run the following as your non-root user:

git clone https://github.com/savonet/liquidsoap-daemon
cd liquidsoap-daemon
./daemonize-liquidsoap.sh

You may be prompted to enter your user's password to authenticate sudo. Once the dæmon is installed, you'll now create a directory structure for storing music, jingles and archives of live streams in your non-root user's home directory. Run the following command:

mkdir -p ~/music/music1 ~/music/jingles ~/archives

Now, open the file main.liq in the liquidsoap-daemon directory. At this point, that file just contains:

output.dummy(blank())

This line sends no audio nowhere, which is not very interesting, so delete that line and add the following base configuration (lines starting with # are comments, so they are ignored by Liquidsoap). This base configuration sets up one music playlist with songs played in random order, jingles inserted approximately every seven songs, smart cross-fading, song requests and automatically recorded live streams. music.mp3, music.ogg and music.opus stream stored music and jingles in MP3, Ogg Vorbis and Ogg Opus formats respectively; stream.mp3, stream.ogg and stream.opus play a live stream when available, falling back to music when the live stream is down:

#Settings
set("server.telnet", true)
set("server.telnet.port", 1234)
set("harbor.bind_addr","0.0.0.0")
# Music playlists
music1 = playlist("~/music/music1")
# Some jingles
jingles = playlist("~/music/jingles")
# If something goes wrong, we'll play this
security = single("~/music/default.ogg")
# Start building the feed with music
radio = random([music1])
# Add the security, requests and smart crossfade
radio = fallback(track_sensitive = false, 
 ↪[smart_crossfade(fallback([request.queue(id="request"),
↪radio])),security])
# Now add some jingles
radio = random(weights = [1, 7],[jingles, radio]) # This plays 
# a jingle once every approximately seven songs, change 7 to 
# another number to change this
# Add a skip command for the music stream
server.register(
usage="skip",
description="Skip the current song.",
"skip",
fun(_) -> begin source.skip(radio) "Done!" end
#Add support for live streams.
live =
audio_to_stereo(input.harbor("live",port=8080,password=
↪"dontpanic1764",buffer=1.0)) #dontpanic1764 is the 
# password used to connect a live stream; it can (and should) be
# different from the source-password in icecast.xml.
full = fallback(track_sensitive=false,
[live,radio])
# Dump archives
file_name = '~/archives/%Y-%m-%d-%H:%M:%S$(if $(title),
↪"-$(title)","").ogg'
output.file(%vorbis,file_name,live,fallible=true)
# Stream it out
output.icecast(%mp3.vbr,
host = "localhost", port = 8000,
password = "dontpanic", mount = "music.mp3",
name="myStation Music Service", description="This is the myStation 
 ↪music stream. Add some information about your station's automated 
 ↪programming.",
radio)
output.icecast(%vorbis,
host = "localhost", port = 8000,
password = "dontpanic", mount = "music.ogg",
name="myStation Music Service", description="This is the myStation 
 ↪music stream. Add some information about your station's 
 ↪automated programming.",
radio)
output.icecast(%opus(vbr="unconstrained",bitrate=60),
host = "localhost", port = 8000,
password = "dontpanic", mount = "music.opus",
name="myStation Music Service", description="This is the myStation 
 ↪music stream. Add some information about your station's 
 ↪automated programming.",
radio)
output.icecast(%mp3.vbr,
host = "localhost", port = 8000,
password = "dontpanic", mount = "stream.mp3",
name="myStation Main Stream", description="The myStation main stream.",
full)
output.icecast(%vorbis, 
host="localhost",port=8000,password="dontpanic",
mount="stream.ogg",
name="myStation Main Stream", description="The myStation main stream.",
full)
output.icecast(%opus(vbr="unconstrained",bitrate=60), 
 ↪description="The myStation main stream.",
host="localhost",port=8000,password="dontpanic",
mount="stream.opus",
full)

Multiple Music Playlists

You may wish to set up multiple music playlists, perhaps with different types of music, and change the frequency at which songs from each playlist are played. To do this, create directories under music for each playlist, named music2, music3 and so on. Then just copy the music1 line in the music playlists section of main.liq, changing the reference to music1 accordingly.

To insert songs randomly from the new playlist every n songs in the stream, add a line below radio = random([music1]), where n represents the approximate number of songs to play before inserting a song from the new playlist:

radio = random(weights = [1, n],[music2, radio])

Here's an example with three music playlists:

# Music playlists
music1 = playlist("~/music/music1")
music2 = playlist("~/music/music2")
music3 = playlist("~/music/music3")
...
radio = random([music1])
radio = random(weights = [1, 6],[music2, radio])
radio = random(weights = [1,12],[music3, radio])

File-Based Playlists

In the base configuration, Liquidsoap will search the directory ~/music/music1 recursively for songs to play. However, you also can give Liquidsoap a newline-delimited text file of paths to songs, either locally on your system or on the web. To do this, simply change the path to a directory to a path to your text file, like this:

music1 = playlist("~/music/music1.pls")

Sequential Playback

By default, Liquidsoap plays tracks in random order. If you want to play tracks sequentially, add mode="sequential" to your playlist definition, like this:

music1 = playlist("~/music/music1",mode="sequential")

Instead of using random (for example, when adding other playlists or jingles), use rotate:

radio = rotate(weights = [1, 7],[jingles, radio])

Sequential playback is best combined with file-based playlists as they both give you total control over the order in which tracks are played by Liquidsoap.

Compression and Normalization

If you'd like to add a more “radio-like” sound to your automated programming, Liquidsoap supports automatic compression and normalization. To compress and normalize the tracks of a playlist or input.harbor live stream, wrap it in an nrj() operator, like so:

music1 = nrj(playlist("~/music/music1"))

Talking Over Automated Programming

You can add a mountpoint allowing you to talk over the automated programming, which will have its volume reduced while you're connected. Add the following to your configuration above #Add support for live streams. The automated programming volume will be changed to 15% of normal while the mic is connected; change p=0.15 to adjust:

# Talk over stream using microphone mount.
mic=input.harbor("mic",port=8080,password="dontpanic1764",buffer=1.0)
radio = smooth_add(delay=0.8, p=0.15, normal=radio, special=mic)

Finishing Up

Edit the configuration as necessary, then save and close the file. Record a file to ~/music/default.ogg; this file will be played when Liquidsoap cannot find other tracks to play. The file should tell listeners that the stream is down and give them information for contacting you to notify you of the problem. Populate the playlist(s) with music, then start Liquidsoap with the following command:

sudo systemctl start liquidsoap

Enable it on boot:

sudo systemctl enable liquidsoap

Once Liquidsoap is started, visit http://example.com:8000 in a web browser (where example.com is the fully qualified domain name of your server). If your system is configured properly, music.mp3, music.ogg and music.opus will appear, playing automated programming. Also, stream.mp3, stream.ogg and stream.opus will play automated programming unless a live stream is connected.

If Icecast appears but no mountpoints are listed, check the Liquidsoap logs at liquidsoap-daemon/log/run.log for errors. If Icecast doesn't load, restart it with systemctl restart icecast2.

To broadcast a live stream through your server, you will need a compatible source client. For Windows, I recommend Altacast (www.altacast.com/index.php/downloads). For Mac users, I suggest Ladiocast, available in the Mac App Store. For Linux, install DarkIce through your system's package manager. On iOS, I recommend iCast. On Android, I suggest Cool Mic. In all cases, use the following configuration:

  • Host: your server's fully qualified domain name.

  • Port: 8080

  • Mount (mountpoint): live (or /live) for a live stream. If you enabled the ability to talk over automated programming, replace live with mic to talk over the music.

  • username: source (some clients don't prompt for a user name, in which case, source is the implied default).

  • password: dontpanic1764 (or the password you specified in the input.harbor configuration).

You can stream in Ogg Vorbis or MP3. Ogg Opus may or may not work, depending on your source client.

Liquidsoap offers control via TCP (over telnet or similar). The base configuration presented in this article enables a song request system and the ability to skip tracks on demand. By default, this interface is available only to users on the local system. The telnet protocol does not support authentication. If you want to make song request functionality available to your users, you'll need to write a program or script customized for your station that interfaces with Liquidsoap.

Connect to Liquidsoap via telnet, like so:

telnet localhost 1234

Once connected, you can request a song with the following, where uri is an absolute path to an audio file on your system or a URL of an audio file on the internet:

request.push uri

To skip the currently playing song and immediately play the next one, simply type skip.

For a list of all available commands, type help, or type help followed by the name of a command for usage information on a particular command.

To end your session, type quit.

In addition to Liquidsoap's telnet interface, Icecast also has a web-based administrative interface that you can use to view listener statistics, kill Liquidsoap's streams or move listeners among mountpoints. Access it at http://example.com:8000/admin (where example.com is your server's fully qualified domain name). Use the admin-user and admin-password you set in icecast.xml.

At this point, you now have a fully functional streaming server that should fit the needs of most users. However, Liquidsoap is extremely flexible, allowing for more exotic setups for special use cases. Refer to the Liquidsoap documentation (liquidsoap.fm/doc-dev/reference.html) for information on additional language features that may be useful to you.

Bill Dengler has been a Linux user and tinkerer since age nine. He was born totally blind due to a rare genetic condition called Norrie Disease, so he relies on a screen reader to access his computer. He is currently pursuing an International Baccalaureate diploma. Feel free to send him questions and comments at codeofdusk@gmail.com.