This part starts from scratch. All you need is a working install of liquidsoap. If you don’t have one already, we can help you; but if you haven’t, check the corresponding page.
Run liquidsoap on one-liners
A good way to test your install and get started is to execute very small liquidsoap programs. Such ``one-liners’’ are also often useful to accomplish simple tasks.
Play a synthesized sound
Simply execute the following command, and you should hear a 440Hz sound on your soundcard:
liquidsoap 'out(sine())'
Did it work? If so, try to modify it:
- Change the pitch. Hint: get the doc of
sine
usingliquidsoap -h sine
or online. - Use a different wave shape, or perhaps some white noise. Hint:
look-up the API in the
Source / Input
section.
Play a remote stream, discover fallibility
Try to execute the liquidsoap expression:
out(input.http("http://ice.rosebud-media.de:8000/88vier"))
You should now be listening to Pi-Radio, unless you have no network
connection, in which case the input.http(...)
source
fails.
The output operator out
is okay with failure: it simply
plays silence when the source fails, waiting for it to be ready again.
But some operators are stricter. If you try
output.ao(input.http("http://ice.rosebud-media.de:8000/88vier"))
the AO output (which uses libao to access your soundcard) warns you
that there might be a failure. To ignore this problem, pass
fallible=true
to the output.
This might seem annoying but it can make sense if you have listeners and you want to make sure that your stream is always up and running for them.
Play a list of files
The playlist
operator can be used to build a source that
plays a list of files. The resulting source will be fallible. You can
either pass a directory name, or a text file containing the list of
files. There are lots of possibilities here, but for now just look at
the mode
parameter; we’ll learn more later.
An interactive example
Suppose we want to be able to request a particular file for playout instead of the automatically chosen files of the playlist. This is achieved by wrapping the playlist in a fallback choice with a request queue:
out(fallback([request.queue(id="q"),playlist("music")]))
If you run this example, you’ll hear your playlist, because the queue
is empty. The queue can be fed through server commands. Enable
the telnet server interface by passing -t
on liquidsoap’s
command line, and connect to it using
telnet localhost 1234
.
- Type
help
, find the command for pushing a request (by its file name) in the queue, and try it. - Find the command for skipping the current track. If you skip a playlist track after having pushed a request in the queue, you should hear that request – unless the request failed to be prepared.
- Find the command for listing the next requests in queue. They are
given by their request id (RID); the commands
request.metadata
andrequest.trace
give you info from the RID. - Also notice commands for listing the next files to be played by the playlist (depends on the playlist more), and reloading the playlist.
- Try setting
track_sensitive=false
for the fallback, see what it does (better, guess what it does from the doc).
Encode a file
You have learned how to build a few sources, synthesizing sound from scratch, from a remote stream or from a list of files. Now, instead of playing the stream directly to your soundcard, we’ll encode it and save it to a local file:
output.file(%vorbis,"test.ogg",source)
Here source
is whatever you want, for example
sine()
.
You can tweak the options of the encoding format, or change the
encoding format; the available options are listed here. You can change the number of
channels (for example, using %vorbis(mono)
) but this may
create problems with a playlist or remote stream, because conversions
are not implicit in liquidsoap; we’ll see later how to deal with
them.
Icecast output
You can now easily change the file example to send you stream to an
icecast server: simply use output.icecast
instead of
output.file
, passing a mount
parameter instead
of a file name, and perhaps overriding the defaults for
host
("127.0.0.1"
) and password
("hackme"
). You just created your first Internet radio
using liquidsoap!
If you have control over the icecast server (or over the network link
to that server) you can simulate a loss of connection. Notice that
liquidsoap only attempts once to reconnect, then fails and shuts down.
For another behavior that tolerates more persistent failures, set
restart=true
.
Using liquidsoap in production
One-liners are good for one-shot uses, but not the most convenient for more complex liquidsoap programs, and for saving/editing the program.
Running a script file
Write the interactive example expression in a file, say
test.liq
.
- You can run it using
liquidsoap -t - < test.liq
, you get the same behavior as before. - If you run it using
liquidsoap -t test.liq
the logs will be written intest.log
in the default logging directory. This can fail as you may not have access to that directory. Change the directory using the setting (see how to get help about that)log.file.path
. Then use the settingslog.stdout
andlog.file
for logging to the terminal and not to a file. - Finally, find the setting for getting rid of the
-t
option on the command line. - You can also use
#!/usr/bin/liquidsoap
(adapt the path) as the first line of your script to directly run./test.liq
instead ofliquidsoap test.liq
(you need tochmod +x test.liq
).
You can also load several scripts and expression on the command line. The last script or expression is taken as the main one, and determines the logging behavior of liquidsoap.
Daemon mode
Finally, the -d
command-line option (or
init.daemon
setting) triggers the daemon mode
where liquidsoap detaches from the terminal to run in the
background.
Checking a media file
To check how liquidsoap sees a file, you can run
liquidsoap -r <FILE>
. Liquidsoap will attempt to
decode the file and its metadata, and compute its duration. This is
(almost) the same process as used during streaming, so it can be used
for checking how something works (or doesn’t work).
Get comfortable with the language
Although it’s easy to forget it when using simple liquidsoap expressions, liquidsoap is a rich programming language. Below is a list of simple exercises to get more comfortable with it. Those exercises do not deal with sources and even less with radio, and might seem pretty dull. But it’s useful to go through them and try to understand what’s going on: it will allow you to avoid the most common mistakes later, when trying to write more complex liquidsoap scripts.
You can write the following examples in a script and execute it, or
type them directly in your terminal, followed by ctrl-D, on the standard
input of liquidsoap -
. You can also pass -c
so
that liquidsoap does not warn you that it has no source to stream.
Using variables
Run the following script:
x = 42
print(x)
As follows:
% liquidsoap --no-pervasives -i /path/to/script.liq
x : int
42
No output defined, nothing to do.
Try to obtain the following types (some help can be found there):
float
bool
string
[string]
(list of strings)(bool*string)
(a pair made of a boolean and a string)
You can redefine a variable:
x = 42
y = "42"
x = (y,y)
z = (x,y)
print(z)
Defining a function
Try this:
def double(s)
s ^ send
print(double("foo"))
Change ^
for +
. Liquidsoap will complain
that it cannot add strings; additions are only for numbers (integers and
floats). Adapt the last line to fix that problem.
Conditionals
A simple example:
if "foo"=="bar" then
print("This is madness.")
else
print("Phew.")
end
It can also be written as follows:
print(
if "foo"=="bar" then
"This is madness."
else
"Phew."
end)
Now, define the variable message
to be the correct
message depending on the test, and finish by
print(message)
. To do this, keep in mind the following: A
variable definition is local to the current scope. Redefining a new
variable does not erase or override previous definitions but only masks
them in the current scope. In other words, definitions should
not be confused with assignments (which are performed by
x=...
in non-functional languages). You’ll learn later how
to use assignments when you really need them.
Sequencing, returning
Here is a function that prints the date and returns 42:
def f()
print(get_process_output("date"))
42
end
# Let's use it:
print(f())
print(1+f())
Note that there is no return statement in liquidsoap (in fact there
is no “statement” at all). Every expression evaluates to a value. A
sequence evaluates to the value of its last expression (42
in the body of the function f
, and
print(1+f())
in the full program). The value ``returned’’
by a function is simply the result of evaluating its body.
Labels and optional parameters
In liquidsoap, functions arguments can be labeled or not. For
example, in f(x,y,foo=z)
we pass x
and
y
as the first two unlabeled arguments, and z
for the argument labeled foo
. Moreover, labeled arguments
can be optional: you saw this with most examples in this page, where
each operator had lots of parameters that you didn’t set (for example,
track_sensitive
in fallback
).
When reading the doc of a function, you see the type of the function,
followed by a description of its arguments. Often you can ignore the
type, but when you write an incorrect script, you might need to read
types to understand error messages. A function type is written
(A1,..,AN)->T
where T
is the type of values
returned by the function and each Ai
specifies one
parameters:
- unlabeled parameters are simply given by their type (for example,
string concatenation has type
(string,string)->string
); - mandatory labeled parameters are written
label:T
whereT
is the type of the parameter; - optional labeled parameters are written
?label:T
.
You will rarely have to define a function with labeled parameters, but if you’re curious you can learn it there.
What you cannot do
Liquidsoap does not have while
and for
loops, nor recursion. This is mostly because they are not really needed
(yet…), notably since functions like list.map
and
list.iter
are often a good replacement.