Hi ! We have prepared an article for our project, liquidsoap.
If you like it, we'd be happy to see it published ! Romain
Liquidsoap
Presentation
Liquidsoap is a powerful audio stream generator designed to work with icecast as a source client. It was built for the campus netradio of the Ecole Normale Supérieure de Lyon. The tough requirements included: accepting user requests, handling remote files and playlists, scheduling special programs, mixing jingles in the stream, interacting via the website or an IRC bot, etc.
The specificity of liquidsoap is that, instead of building an ad-hoc solution for that single netradio, it was developped as a flexible tool in which you can combine as you like the features that you want. Hence, it has then been used successfully for several other netradios, for which the usual streaming tools did not suffice.
Design
Describing an audio stream can be very complex: several inputs (file, stream relay, soundcard) can come into play, they can be combined in various ways (audio processing, mixing, track scheduling, fallbacks) and finally output in various ways (several servers/contents/formats). To make it easy without loosing in expressivity, liquidsoap configurations are written in its own little script language.
That language has a notion of audio stream, request, and has builtin functions for combining streams in various ways. Some of its main features are:
- It is statically typed: your stream never crashes because you made a typo in a dark corner of the conf.
- Types are inferred: you don't have to write them.
- It is functional: you can define your own compound operations, but functions are also used to describe transitions from one stream to another.
Liquidsoap is quite versatile: it can be used as a daemon or as a one-line tool, and is not restricted to streaming to an icecast server, but can also stream to your local soundcard!
Usage
If you are not aware of the way a web radio can be built using icecast, you may want to read some more documentation on this topic. In short, icecast accepts sources connections, and relays the data from these sources to its listeners. Liquidsoap acts as a source.
Let's begin with a very simple example.. Let's say that you run an icecast server at host 'myhost' with source's passowrd being 'hackmeimcool', and you want to stream in Ogg/Vorbis a single looped file (Ogg/Vorbis, MP3, WAV, even AAC on the SVN version). Then this may do the job for you:
liquidsoap 'output.icecast.vorbis(host="myhost",password="hackmeimcool",\
mount="mystream.ogg",single("/path/to/my/file"))'
Now a more complex example.. Let's say that you want to stream all the files contained in a given directory, but relay a live stream as soon as it is available. At this point we need to introduce the notion of 'faillible source'. As we said before, the most important constraint when streaming is to be able to provide data at anytime. Liquidsoap checks this property when starting. For instance, playing a list of files is not safe unless you check that the files are local and valid first, etc..
So, the basic way to start a complex stream is to define a fallback source, which can be a single file (single files are checked at start-up):
emergency = single("/path/to/my/file")
Now define your playlist:
main = playlist("/path/to/playlist/directory")
At this point, you will also create the other source that you want to stream as soon as it is available:
live = input.http("http://host:8000/live.ogg")
For instance this source may be a live show streamed to this mountpoint.
Then you can write the full script, where /usr/bin/liquidsoap is the actual place for the binary:
#!/usr/bin/liquidsoap
# Set logging to stdout
set log.dir = "/tmp"
set log.stdout = true
# fallback source:
emergency = single("/path/to/my/file")
# Playlist:
main = playlist("/path/to/playlist/directory")
# Live stream
live = input.http("http://host:8000/live.ogg")
# Fallback from fallback source to playlist:
# The "track_sensitive" parameter is set if we want to wait for the end of
# current song before switching.
source = fallback(track_sensitive=true,[main,emergency])
# Fallback from playlist to live:
# No track_sensitivity here since we do not want to cut the
# begining of the live show... !
source = fallback(track_sensitive=false,[live,source])
# Output source to icecast:
output.icecast.vorbis(host="myhost",password="hackmeimcool",
mount="mystream.ogg",source)
Conclusion
The complete API is available here. We have not presented many cool features, such as:
- sound effects;
- custom transitions;
- sound card I/O;
- interaction with other components (website, external scheduler) in a more complex framework;
- many others..
Liquidsoap is already used in various netradios, suchs as dolebrai which streams libre music, and RadioPi which has a complex multi-server and multi-stream configuration.
This presentation of liquidsoap was very very short... This is not the place for a tutorial, let's finish instead with a poweruser example, which I hope will make you eager to learn more. Just copy-paste and run, there's nothing to edit!
#!/usr/bin/liquidsoap
set log.stdout = true
set log.dir = "/tmp"
set telnet = true
# Define a numbers source that plays a number station,
# or files from a queue that can be fed by the user using telnet.
numbers = input.http("http://zenodore.streaming.radiopytagor.net:8000/numbers-station.ogg")
numbers = strip_blank(length=2.,threshold=-30.,numbers)
numbers = fallback([normalize(request.queue(id="q")),numbers])
# We relay the Dolebraï libre music netradio.
# We wrap it in mksafe(), a predefined operator that plays
# blank when dolebrai fails to relay anything.
normal = mksafe(input.http("http://dolebrai.net:8000/dolebrai.ogg"))
# And now the magic :)
def smooth_add(~normal,~special)
d = 1. # delay before mixing after beginning of mix
p = 0.2 # portion of normal when mixed
fade.final = fade.final(duration=d*.2.)
fade.initial = fade.initial(duration=d*.2.)
q = 1. -. p
# We alias change_volume to c
c = change_volume
fallback(track_sensitive=false,
[special,normal],
transitions=[
fun(normal,special)->
add(normalize=false,[sequence([blank(duration=d),c(q,special)]),
c(q,fade.final(normal)),
c(p,normal)]),
fun(special,normal)->
add(normalize=false,[c(p,normal),c(q,fade.initial(normal))])
])
end
# out is a predefined operator that
# outputs to your local sound card using libao
out(smooth_add(normal=normal,special=numbers))
Usage:
- copy-paste this to test.liq
- make it executable
- Run it !
Optionally you can test the following:
- telnet localhost 1234
- q.push /path/to/file
And listen !
If you are interested in a very flexible way to design your audio stream, feel free to visit our website, drop us a mail at [EMAIL PROTECTED] or join our IRC channel, #savonet on freenode. Liquidsoap is available in debian testing and unstable.
