-updated liquidsoap scripts + library to be liquidsoap-beta2 compliant
This commit is contained in:
parent
022b013dd2
commit
3dc1380fab
|
@ -71,7 +71,7 @@ def output.icecast.lame(
|
|||
~host="localhost",~port=8000,
|
||||
~user="source",~password="hackme",
|
||||
~genre="Misc",~url="http://savonet.sf.net/",
|
||||
~description="OCaml Radio!",~public=true,
|
||||
~description="Liquidsoap Radio!",~public=true,
|
||||
~dumpfile="",~mount="Use [name]",
|
||||
~name="Use [mount]",~protocol="http",
|
||||
~lame="lame",~bitrate=128,~swap=false,
|
||||
|
@ -130,7 +130,7 @@ def output.shoutcast.lame(
|
|||
~host="localhost",~port=8000,
|
||||
~user="source",~password="hackme",
|
||||
~genre="Misc",~url="http://savonet.sf.net/",
|
||||
~description="OCaml Radio!",~public=true,
|
||||
~description="Liquidsoap Radio!",~public=true,
|
||||
~dumpfile="",~name="Use [mount]",~icy_reset=true,
|
||||
~lame="lame",~aim="",~icq="",~irc="",
|
||||
~fallible=false,~on_start={()},~on_stop={()},
|
||||
|
@ -177,7 +177,7 @@ def output.icecast.flac(
|
|||
~host="localhost",~port=8000,
|
||||
~user="source",~password="hackme",
|
||||
~genre="Misc",~url="http://savonet.sf.net/",
|
||||
~description="OCaml Radio!",~public=true,
|
||||
~description="Liquidsoap Radio!",~public=true,
|
||||
~dumpfile="",~mount="Use [name]",
|
||||
~name="Use [mount]",~protocol="http",
|
||||
~flac="flac",~quality=6,
|
||||
|
@ -240,7 +240,7 @@ def output.icecast.aacplusenc(
|
|||
~host="localhost",~port=8000,
|
||||
~user="source",~password="hackme",
|
||||
~genre="Misc",~url="http://savonet.sf.net/",
|
||||
~description="OCaml Radio!",~public=true,
|
||||
~description="Liquidsoap Radio!",~public=true,
|
||||
~dumpfile="",~mount="Use [name]",
|
||||
~name="Use [mount]",~protocol="http",
|
||||
~aacplusenc="aacplusenc",~bitrate=64,
|
||||
|
@ -288,7 +288,7 @@ def output.shoutcast.aacplusenc(
|
|||
~host="localhost",~port=8000,
|
||||
~user="source",~password="hackme",
|
||||
~genre="Misc",~url="http://savonet.sf.net/",
|
||||
~description="OCaml Radio!",~public=true,
|
||||
~description="Liquidsoap Radio!",~public=true,
|
||||
~fallible=false,~on_start={()},~on_stop={()},
|
||||
~dumpfile="",~name="Use [mount]",~icy_reset=true,
|
||||
~aim="",~icq="",~irc="",~aacplusenc="aacplusenc",
|
||||
|
|
|
@ -28,8 +28,6 @@ def enable_external_flac_decoder() =
|
|||
# If the value is not an int, this returns 0 and we are ok :)
|
||||
int_of_string(channels)
|
||||
else
|
||||
# Try to detect using mime test..
|
||||
mime = get_mime(file)
|
||||
if string.match(pattern="flac",file) then
|
||||
# We do not know the number of audio channels
|
||||
# so setting to -1
|
||||
|
@ -76,7 +74,7 @@ end
|
|||
|
||||
%ifdef add_oblivious_decoder
|
||||
# Enable or disable external FAAD (AAC/AAC+/M4A) decoders.
|
||||
# Requires faad binary in the path for audio decoding and
|
||||
# Requires faad binary in the path for audio decoding and
|
||||
# metaflac binary for metadata. Does not work on Win32.
|
||||
# Please note that built-in support for faad is available
|
||||
# in liquidsoap if compiled and should be preferred over
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
# BIG TODO:
|
||||
# - Check for errors
|
||||
# - Unregister radio and streams
|
||||
|
||||
# Register a radio on Liquidsoap Flows.
|
||||
# @category Liquidsoap
|
||||
# @param ~radio Name of the radio.
|
||||
# @param ~website URL of the website of the radio.
|
||||
# @param ~description Description of the radio.
|
||||
# @param ~genre Genre of the radio (rock or rap or etc.).
|
||||
# @param ~streams List of streams for the radio described by \
|
||||
# a pair of strings consisting of the format of the stream \
|
||||
# and the url of the stream. The format should be \
|
||||
# of the form "ogg/128k" consisting of the codec and \
|
||||
# the bitrate, separated by "/".
|
||||
def register_flow(~server="",~user="default",~password="default",
|
||||
~email="",~radio,~website,~description,~genre,
|
||||
~streams,s)
|
||||
|
||||
# If the server is "", we get the server from sf.net
|
||||
server =
|
||||
if server == "" then
|
||||
server = http.get("http://savonet.sourceforge.net/flows_server")
|
||||
html_status = snd(fst(fst(fst(server))))
|
||||
if html_status == 200 then
|
||||
snd(server)
|
||||
else
|
||||
# If sf is down, we use the hardcoded server
|
||||
"http://savonet.rastageeks.org/liqflows.py"
|
||||
end
|
||||
else
|
||||
server
|
||||
end
|
||||
log(level=4,"Flows server: #{server}")
|
||||
|
||||
# Initial variables
|
||||
ping_period = 600. # Pinging period in seconds
|
||||
|
||||
# Fix default parameters
|
||||
# and set request function.
|
||||
base_params = [("v", "0.0"),
|
||||
("user",user),
|
||||
("password",password),
|
||||
("email",email),
|
||||
("radio",radio)]
|
||||
def request(~cmd,~params) =
|
||||
log = log(label=radio)
|
||||
log(level=4,"Processing command #{cmd} with arguments:")
|
||||
def log_arg(x) =
|
||||
label = fst(x)
|
||||
value = snd(x)
|
||||
log(level=4," #{label}: #{value}")
|
||||
end
|
||||
list.iter(log_arg,params)
|
||||
|
||||
cmd = url.encode(cmd)
|
||||
params = list.append(base_params,params)
|
||||
def f(z) =
|
||||
x = fst(z)
|
||||
y = url.encode(snd(z))
|
||||
"#{x}=#{y}"
|
||||
end
|
||||
params = string.concat(separator="&",list.map(f,params))
|
||||
url = "#{server}?cmd=#{cmd}&#{params}"
|
||||
|
||||
# TODO: do something with errors!
|
||||
answer = http.get(url)
|
||||
x = fst(answer)
|
||||
status = fst(x)
|
||||
y = fst(status)
|
||||
protocol = fst(y)
|
||||
code = snd(y)
|
||||
desc = snd(status)
|
||||
headers = snd(x)
|
||||
data = snd(answer)
|
||||
log(level=4,"Response status: #{protocol} #{code} #{desc}")
|
||||
log(level=4,"Response headers:")
|
||||
list.iter(log_arg,headers)
|
||||
log(level=4,"Response content: #{data}")
|
||||
end
|
||||
|
||||
# Register radio
|
||||
params = [("radio_website",website),
|
||||
("radio_description",description),
|
||||
("radio_genre",genre)]
|
||||
request(cmd="add radio",params=params)
|
||||
|
||||
# Ping
|
||||
def ping() =
|
||||
ignore(request(cmd="ping radio",params=[]))
|
||||
ping_period
|
||||
end
|
||||
add_timeout(fast=false,ping_period,ping)
|
||||
|
||||
# Register streams
|
||||
def register_stream(format_url)
|
||||
format = fst(format_url);
|
||||
url = snd(format_url);
|
||||
params = [("stream_format",format),("stream_url",url)]
|
||||
request(cmd="add stream",params=params)
|
||||
end
|
||||
request(cmd="clear streams",params=[])
|
||||
list.iter(register_stream,streams)
|
||||
|
||||
# Metadata update
|
||||
def metadata(m) =
|
||||
artist = m["artist"]
|
||||
title = m["title"]
|
||||
params = [("m_title",title),("m_artist",artist)]
|
||||
request(cmd="metadata",params=params)
|
||||
end
|
||||
on_metadata(metadata,s)
|
||||
end
|
|
@ -1,15 +0,0 @@
|
|||
# Launch with: screen -c interactive.screen
|
||||
screen -t Liquidsoap liquidsoap --interactive 'set("log.file.path","/tmp/interactive.log") system("echo \"setenv PID #{getpid()}\" > /tmp/interactive.env")'
|
||||
verbose
|
||||
# Yeah, this is a trick
|
||||
# to wait for interactive.env
|
||||
# to be created
|
||||
logfile /dev/null
|
||||
log
|
||||
source /tmp/interactive.env
|
||||
screen -t Log tail --pid=$PID -f /tmp/interactive.log
|
||||
split -v
|
||||
select 0
|
||||
focus
|
||||
select 1
|
||||
focus
|
|
@ -1,43 +0,0 @@
|
|||
#!/sbin/runscript
|
||||
|
||||
user=liquidsoap
|
||||
group=liquidsoap
|
||||
prefix=/usr/local
|
||||
exec_prefix=${prefix}
|
||||
confdir=${prefix}/etc/liquidsoap
|
||||
liquidsoap=${exec_prefix}/bin/liquidsoap
|
||||
rundir=${prefix}/var/run/liquidsoap
|
||||
|
||||
depend() {
|
||||
after net icecast
|
||||
}
|
||||
|
||||
start() {
|
||||
cd $confdir
|
||||
for liq in *.liq ; do
|
||||
if test $liq != '*.liq' ; then
|
||||
ebegin "Starting $liq"
|
||||
start-stop-daemon --start --quiet --pidfile $rundir/${liq%.liq}.pid \
|
||||
--chuid $user:$group --exec $liquidsoap -- -d $confdir/$liq
|
||||
eend $?
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
stop() {
|
||||
cd $rundir
|
||||
for liq in *.pid ; do
|
||||
if test $liq != '*.pid' ; then
|
||||
ebegin "Stopping $liq"
|
||||
start-stop-daemon --stop --quiet --pidfile $liq
|
||||
eend $?
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
restart() {
|
||||
svc_stop
|
||||
einfo "Sleeping 4 seconds ..."
|
||||
sleep 4
|
||||
svc_start
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
#!/bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: liquidsoap
|
||||
# Required-Start: $remote_fs $network $time
|
||||
# Required-Stop: $remote_fs $network $time
|
||||
# Should-Start:
|
||||
# Should-Stop:
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Starts the liquidsoap daemon
|
||||
# Description:
|
||||
### END INIT INFO
|
||||
|
||||
user=liquidsoap
|
||||
group=liquidsoap
|
||||
prefix=/usr/local
|
||||
exec_prefix=${prefix}
|
||||
confdir=${prefix}/etc/liquidsoap
|
||||
liquidsoap=${exec_prefix}/bin/liquidsoap
|
||||
rundir=${prefix}/var/run/liquidsoap
|
||||
|
||||
# Test if $rundir exists
|
||||
if [ ! -d $rundir ]; then
|
||||
mkdir -p $rundir;
|
||||
chown $user:$group $rundir
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
stop)
|
||||
echo -n "Stopping channels: "
|
||||
cd $rundir
|
||||
for liq in *.pid ; do
|
||||
if test $liq != '*.pid' ; then
|
||||
echo -n "$liq "
|
||||
start-stop-daemon --stop --quiet --pidfile $liq --retry 4
|
||||
fi
|
||||
done
|
||||
echo "OK"
|
||||
;;
|
||||
|
||||
start)
|
||||
echo -n "Starting channels: "
|
||||
cd $confdir
|
||||
for liq in *.liq ; do
|
||||
if test $liq != '*.liq' ; then
|
||||
echo -n "$liq "
|
||||
start-stop-daemon --start --quiet --pidfile $rundir/${liq%.liq}.pid \
|
||||
--chuid $user:$group --exec $liquidsoap -- -d $confdir/$liq
|
||||
fi
|
||||
done
|
||||
echo "OK"
|
||||
;;
|
||||
|
||||
restart|force-reload)
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|force-reload}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
|
@ -1,15 +0,0 @@
|
|||
/usr/local/var/log/liquidsoap/*.log {
|
||||
compress
|
||||
rotate 5
|
||||
size 300k
|
||||
missingok
|
||||
notifempty
|
||||
sharedscripts
|
||||
postrotate
|
||||
for liq in /usr/local/var/run/liquidsoap/*.pid ; do
|
||||
if test $liq != '/usr/local/var/run/liquidsoap/*.pid' ; then
|
||||
start-stop-daemon --stop --signal USR1 --quiet --pidfile $liq
|
||||
fi
|
||||
done
|
||||
endscript
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This script is called from liquidsoap for generating a file
|
||||
# for "say:voice/text" URIs.
|
||||
# Usage: liquidtts text output_file voice
|
||||
|
||||
echo $1 | /usr/bin/text2wave -f 44100 > $2.tmp.wav && /usr/bin/sox $2.tmp.wav -t wav -c 2 -r 44100 $2 2> /dev/null > /dev/null
|
||||
return=$?
|
||||
/bin/rm $2.tmp.wav
|
||||
false $2 2> /dev/null > /dev/null
|
||||
exit $return
|
|
@ -2,3 +2,4 @@
|
|||
%include "externals.liq"
|
||||
%include "shoutcast.liq"
|
||||
%include "lastfm.liq"
|
||||
%include "flows.liq"
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# @param ~on_stop Callback executed when outputting stops.
|
||||
# @param ~on_connect Callback executed when connection starts.
|
||||
# @param ~on_disconnect Callback executed when connection stops.
|
||||
# @param ~icy_metadata Send new metadata using the ICY protocol. One of: "guess", "true", "false"
|
||||
# @param ~icy_metadata Send new metadata using the ICY protocol. One of: "guess", "true", "false"
|
||||
# @param ~format Format, e.g. "audio/ogg". When empty, the encoder is used to guess.
|
||||
# @param e Endoding format. For shoutcast, should be mp3 or AAC(+).
|
||||
# @param s The source to output
|
||||
|
@ -24,7 +24,7 @@ def output.shoutcast(
|
|||
~host="localhost",~port=8000,
|
||||
~user="source",~password="hackme",
|
||||
~genre="Misc",~url="http://savonet.sf.net/",
|
||||
~name="OCaml Radio!",~public=true, ~format="",
|
||||
~name="Liquidsoap Radio!",~public=true, ~format="",
|
||||
~dumpfile="", ~icy_metadata="guess",
|
||||
~on_connect={()}, ~on_disconnect={()},
|
||||
~aim="",~icq="",~irc="",~icy_reset=true,
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# This is the test for bug #403 from our old trac.
|
||||
#
|
||||
# Make a switch() declare itself ready and arrange to use it for the next
|
||||
# frame where it isn't ready anymore.
|
||||
#
|
||||
# Two switches A and B
|
||||
# A is only ready for a short period of time due to its predicates.
|
||||
# B reselects at the end of a frame just before A becomes unavailable
|
||||
# as a result, B has selected = A, attempts to stream it
|
||||
# but A finds itself not ready anymore.
|
||||
# In other words, B committed but A did not.
|
||||
|
||||
r = ref false
|
||||
pred = { v=!r ; r:=false ; v }
|
||||
add_timeout(2.,{ r := true ; (-1.) })
|
||||
|
||||
mixer = fallback(id="mixer", track_sensitive=false,
|
||||
[at(pred, sine(duration=3.)), blank()])
|
||||
|
||||
output.dummy(mixer)
|
||||
add_timeout(3.,{ print("TEST PASSED") ; shutdown() ; (-1.) })
|
|
@ -0,0 +1,10 @@
|
|||
# In LS-268 we realized that an incorrect assumption had
|
||||
# been made in code from LS-394, resulting in a crash in
|
||||
# case of source re-awakening.
|
||||
|
||||
p = input.http("http://localhost:8000/nonexistent")
|
||||
o = output.dummy(fallible=true,p)
|
||||
|
||||
add_timeout(2.,{ source.shutdown(o) ; (-1.) })
|
||||
add_timeout(3.,{ output.dummy(fallible=true,p) ; (-1.) })
|
||||
add_timeout(4.,{ shutdown() ; (-1.) })
|
|
@ -0,0 +1,11 @@
|
|||
s = on_track(
|
||||
fun(_)-> begin print("TEST PASSED") shutdown() end,
|
||||
blank(duration=1.))
|
||||
|
||||
r = ref false
|
||||
d = source.dynamic({ if !r then [s] else [] end })
|
||||
|
||||
output.dummy(mksafe(d))
|
||||
|
||||
add_timeout(2.,{r:=true;(-1.)})
|
||||
add_timeout(4.,{print("TEST FAILED");shutdown();(-1.)})
|
|
@ -0,0 +1,15 @@
|
|||
s1 = fail()
|
||||
s2 = on_track(
|
||||
fun(_)-> begin print("TEST PASSED") shutdown() end,
|
||||
blank(duration=1.))
|
||||
|
||||
r = ref 0
|
||||
d = source.dynamic({ if !r==1 then [s1]
|
||||
elsif !r==2 then [s2]
|
||||
else [] end })
|
||||
|
||||
output.dummy(mksafe(d))
|
||||
|
||||
add_timeout(2.,{r:=1;(-1.)})
|
||||
add_timeout(3.,{r:=2;(-1.)})
|
||||
add_timeout(5.,{print("TEST FAILED");shutdown();(-1.)})
|
|
@ -0,0 +1,17 @@
|
|||
# Scenario:
|
||||
# Let foo start using q, then stop it, skip in q.
|
||||
# When foo restarts it doesn't know that q isn't ready anymore,
|
||||
# which can lead to a crash.
|
||||
|
||||
q = once(sine(duration=10.))
|
||||
output.dummy(id="bar",mksafe(q))
|
||||
output.dummy(id="foo",fallback([amplify(1.,q),blank(duration=1.)]))
|
||||
|
||||
def at(t,s)
|
||||
add_timeout(t,{ignore(server.execute(s));(-1.)})
|
||||
end
|
||||
|
||||
at(3.,"foo.stop")
|
||||
at(4.,"bar.skip")
|
||||
at(5.,"foo.start")
|
||||
add_timeout(6.,{print("TEST PASSED");shutdown();(-1.)})
|
|
@ -0,0 +1,13 @@
|
|||
# In LS-503 we realized that a source may throw an
|
||||
# exception during output_get_ready call in the initial
|
||||
# main phase. This code reproduces the issue by throwing
|
||||
# an exception in output.icecast.
|
||||
|
||||
# Reopen stderr to /dev/null to
|
||||
# disable printing expected exception
|
||||
reopen.stderr("/dev/null")
|
||||
|
||||
p = input.http("http://localhost:8000/nonexistent")
|
||||
o = output.icecast(%wav,fallible=true,host="nonexistent",
|
||||
mount="test",p)
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
DISTFILES = Makefile $(wildcard *.liq) $(wildcard *.pl)
|
||||
|
||||
top_srcdir = ../..
|
||||
include $(top_srcdir)/Makefile.rules
|
||||
|
||||
test:
|
||||
@for i in $(wildcard *.liq) ; do \
|
||||
echo -n "$$i... " ; $(top_srcdir)/src/liquidsoap -q - < ./$$i | head -n 1 ; \
|
||||
done
|
||||
@echo -n "type_errors.pl... " ; \
|
||||
if (./type_errors.pl > /dev/null 2> /dev/null) ; then \
|
||||
echo "TEST PASSED (check manually the prettiness of messages)" ; \
|
||||
else \
|
||||
echo "TEST FAILED" ; \
|
||||
fi
|
|
@ -0,0 +1,43 @@
|
|||
count = ref 1
|
||||
fail = ref false
|
||||
|
||||
def echo(s)
|
||||
# system("echo "^quote(s))
|
||||
if s != string_of(!count) then
|
||||
fail := true
|
||||
end
|
||||
count := !count + 1
|
||||
()
|
||||
end
|
||||
|
||||
def test(lbl,f)
|
||||
if f() then echo(lbl) else echo("fail "^lbl) end
|
||||
end
|
||||
|
||||
test("1",{ 1==1 })
|
||||
test("2",{ 1+1==2 })
|
||||
test("3",{ (-1)+2==1 })
|
||||
test("4",{ (-1)+2 <= 3*2 })
|
||||
test("5",{ true })
|
||||
test("6",{ true and true })
|
||||
test("7",{ 1==1 and 1==1 })
|
||||
test("8",{ (1==1) and (1==1) })
|
||||
test("9",{ true and (-1)+2 <= 3*2 })
|
||||
|
||||
l = [ ("bla",""), ("bli","x"), ("blo","xx"), ("blu","xxx"), ("dix","10") ]
|
||||
echo(l["dix"])
|
||||
test("11",{ 2 == list.length(string.split(separator="",l["blo"])) })
|
||||
|
||||
%ifdef foobarbaz
|
||||
if = if is not a well-formed expression, and we do not care...
|
||||
%endif
|
||||
|
||||
echo("1#{1+1}")
|
||||
echo(string_of(int_of_float(float_of_string(default=13.,"blah"))))
|
||||
|
||||
f=fun(x)->x
|
||||
# Checking that the following is not recursive:
|
||||
f=fun(x)->f(x)
|
||||
echo(string_of(f(14)))
|
||||
|
||||
if !fail then print("TEST FAILED") else print("TEST PASSED") end
|
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
use strict ;
|
||||
|
||||
my $liquidsoap = "../../src/liquidsoap";
|
||||
die unless -f $liquidsoap ;
|
||||
|
||||
$liquidsoap = "$liquidsoap -c";
|
||||
|
||||
sub section {
|
||||
print "\n*** $_[0] ***\n\n" ;
|
||||
}
|
||||
|
||||
sub incorrect {
|
||||
my $expr = pop ;
|
||||
print "Incorrect expression $expr...\n" ;
|
||||
system "$liquidsoap '$expr'" ;
|
||||
die unless (($?>>8)==1) ;
|
||||
print "\n" ;
|
||||
}
|
||||
|
||||
sub correct {
|
||||
my $expr = pop ;
|
||||
print "Correct expression $expr...\n" ;
|
||||
system "$liquidsoap -i '$expr'" ;
|
||||
die unless (($?>>8)==0) ;
|
||||
print "\n";
|
||||
}
|
||||
|
||||
section("BASIC");
|
||||
incorrect('[1]==["1"]');
|
||||
incorrect('1==["1"]');
|
||||
incorrect('1==(1,"1")');
|
||||
# In some of those examples the type error could be reported for a
|
||||
# sub-expression since we have location information.
|
||||
# With the concise error, it's still pretty good currently.
|
||||
incorrect('(1,1)==(1,"1")');
|
||||
incorrect('(1,1)==("1",1)');
|
||||
incorrect('1==request.create("")');
|
||||
incorrect('fun(x)->x(snd(x))');
|
||||
|
||||
section("SUBTYPING");
|
||||
incorrect('(1:unit)');
|
||||
correct('((blank():source(1,1,1)):source(*,*,*))');
|
||||
incorrect('((blank():source(*,*,*)):source(1,1,1))');
|
||||
# Next one requires the inference of a subtype (fixed vs. variable arity)
|
||||
correct('audio_to_stereo(add([]))');
|
||||
|
||||
section("CONSTRAINTS");
|
||||
incorrect('"bl"+"a"');
|
||||
incorrect('(fun(a,b)->a+b)==(fun(a,b)->a+b)');
|
||||
incorrect('fun(x)->x(x)'); # TODO is it an accident that we get same varname
|
||||
incorrect('def f(x) y=snd(x) y(x) end');
|
||||
|
||||
section("LET GENERALIZATION");
|
||||
correct('def f(x) = y=x ; y end f(3)+snd(f((1,2)))');
|
||||
incorrect('def f(x) = y=x ; y end f(3)+"3"');
|
||||
|
||||
section("ARGUMENTS");
|
||||
# The errors should be about the type of the param, not of the function.
|
||||
incorrect('1+"1"');
|
||||
# Also, a special simple error is expected for obvious labelling mistakes.
|
||||
incorrect('fallback(transitions=[],xxxxxxxxxxx=[])');
|
||||
incorrect('fallback(transitions=[],transitions=[])');
|
||||
|
||||
section("FUNCTIONS");
|
||||
incorrect('fallback(transitions=[fun(~l)->1])');
|
||||
incorrect('fallback(transitions=[fun(~l=1)->1])');
|
||||
incorrect('fallback(transitions=[fun(x,y=blank())->y])');
|
||||
incorrect('fallback(transitions=[fun(x,y)->0])');
|
||||
correct('f=fallback(transitions=[fun(x,y,a=2)->x])');
|
||||
incorrect('fallback(transitions=[fun(x,y)->y+1])');
|
||||
correct('x=fun(f)->f(3) y=x(fun(f,u="1")->u)');
|
||||
|
||||
section("CONTENT KIND");
|
||||
incorrect('output.file(%vorbis(stereo),"foo",mean(blank()))');
|
||||
incorrect('output.file(%vorbis(stereo),"foo",video.add_image(blank()))');
|
||||
incorrect('def f(x) = output.file(%vorbis(stereo),"",x) output.file(%vorbis(mono),"",x) end');
|
||||
incorrect('add([output.file(%vorbis(stereo),"",blank()),output.file(%vorbis(mono),"",blank())])');
|
||||
incorrect('add([mean(blank()),audio_to_stereo(add([]))])');
|
||||
|
||||
print "Everything's good!\n" ;
|
|
@ -0,0 +1,112 @@
|
|||
# Check these examples with: liquidsoap --no-libs -i -c typing.liq
|
||||
|
||||
# TODO Throughout this file, parsing locations displayed in error messages
|
||||
# are often much too inaccurate.
|
||||
|
||||
# Check that some polymorphism is allowed.
|
||||
# id : (string,'a)->'a
|
||||
def id(a,b)
|
||||
log(a)
|
||||
b
|
||||
end
|
||||
ignore("bla"==id("bla","bla"))
|
||||
ignore(0==id("zero",0))
|
||||
|
||||
# Reporting locations for the next error is non-trivial, because it is about
|
||||
# an instantiation of the type of id. The deep error doesn't have relevant
|
||||
# information about why the int should be a string, the outer one has.
|
||||
# id(0,0)
|
||||
|
||||
# Polymorphism is limited to outer generalizations, this is not system F.
|
||||
# apply : ((string)->'a)->'a
|
||||
def apply(f)
|
||||
f("bla")
|
||||
# f is not polymorphic, the following is forbidden:
|
||||
# f(0)
|
||||
# f(f)
|
||||
end
|
||||
|
||||
# The level checks forbid abusive generalization.
|
||||
# id' : ('a)->'a
|
||||
def id'(x)
|
||||
# If one isn't careful about levels/scoping, f2 gets the type ('a)->'b
|
||||
# and so does twisted_id.
|
||||
def f2(y)
|
||||
x
|
||||
end
|
||||
f2(x)
|
||||
end
|
||||
|
||||
# More errors...
|
||||
# 0=="0"
|
||||
# [3,""]
|
||||
|
||||
# Subtyping, functions and lists.
|
||||
f1 = fun () -> 3
|
||||
f2 = fun (a=1) -> a
|
||||
|
||||
# This is OK, l1 is a list of elements of type f1.
|
||||
l1 = [f1,f2]
|
||||
list.iter(fun (f) -> log(string_of(f())), l1)
|
||||
# Forbidden. Indeed, f1 doesn't accept any argument -- although f2 does.
|
||||
# Here the error message may even be too detailed since it goes back to the
|
||||
# definition of l1 and requires that f1 has type (int)->int.
|
||||
# list.iter(fun (f) -> log(string_of(f(42))), l1)
|
||||
|
||||
# Actually, this is forbidden too, but the reason is more complex...
|
||||
# The infered type for the function is ((int)->int)->unit,
|
||||
# and (int)->int is not a subtype of (?int)->int.
|
||||
# There's no most general answer here since (?int)->int is not a
|
||||
# subtype of (int)->int either.
|
||||
# list.iter(fun (f) -> log(string_of(f(42))), [f2])
|
||||
|
||||
# Unlike l1, this is not OK, since we don't leave open subtyping constraints
|
||||
# while infering types.
|
||||
# I hope we can make the inference smarter in the future, without obfuscating
|
||||
# the error messages too much.
|
||||
# The type error here shows the use of all the displayed positions:
|
||||
# f1 has type t1, f2 has type t2, t1 should be <: t2
|
||||
# l2 = [ f2, f1 ]
|
||||
|
||||
# An error where contravariance flips the roles of both sides..
|
||||
# [fun (x) -> x+1, fun (y) -> y^"."]
|
||||
|
||||
# An error without much locations..
|
||||
# TODO An explaination about the missing label would help a lot here.
|
||||
# def f(f)
|
||||
# f(output.icecast.vorbis)
|
||||
# f(output.icecast.mp3)
|
||||
# end
|
||||
|
||||
# This causes an occur-check error.
|
||||
# TODO The printing of the types breaks the sharing of one EVAR
|
||||
# across two types. Here the sharing is actually the origin of the occur-check
|
||||
# error. And it's not easy to understand..
|
||||
# omega = fun (x) -> x(x)
|
||||
|
||||
# Now let's test ad-hoc polymorphism.
|
||||
|
||||
echo = fun(x) -> system("echo #{quote(string_of(x))}")
|
||||
|
||||
ignore("bla")
|
||||
ignore((1,3.12))
|
||||
ignore(1 + 1)
|
||||
ignore(1. + 2.14)
|
||||
|
||||
# string is not a Num
|
||||
# echo("bl"+"a")
|
||||
|
||||
ignore(1 <= 2)
|
||||
ignore((1,2) == (1,3))
|
||||
|
||||
# float <> int
|
||||
# echo(1 == 2.)
|
||||
|
||||
# source is not an Ord
|
||||
# echo(blank()==blank())
|
||||
|
||||
def sum_eq(a,b)
|
||||
a+b == a
|
||||
end
|
||||
|
||||
print("TEST PASSED")
|
|
@ -0,0 +1,61 @@
|
|||
# run this file through "liquidsoap -c -i"
|
||||
# to see if pretty printing looks pretty enough
|
||||
# for more checks, pass --twidth 78
|
||||
|
||||
x = (blank() : source(2,3,4))
|
||||
pair = (x,x)
|
||||
x = (x,x)
|
||||
x = (x,x)
|
||||
x = (x,x)
|
||||
|
||||
def output.shoutcast(
|
||||
~id="output.shoutcast",~start=true,
|
||||
~restart=false,~restart_delay=3,
|
||||
~host="localhost",~port=8000,
|
||||
~user="source",~password="hackme",
|
||||
~genre="Misc",~url="http://savonet.sf.net/",
|
||||
~name="OCaml Radio!",~public=true, ~format="",
|
||||
~dumpfile="", ~icy_metadata="guess",
|
||||
~on_connect={()}, ~on_disconnect={()},
|
||||
~aim="",~icq="",~irc="",~icy_reset=true,
|
||||
~fallible=false,~on_start={()},~on_stop={()},
|
||||
e,s)
|
||||
=
|
||||
42
|
||||
end
|
||||
|
||||
x = (output.shoutcast,pair)
|
||||
|
||||
def output.shoutcast(
|
||||
~id="output.shoutcast",~start=true,
|
||||
~restart=false,~restart_delay=3,
|
||||
~host="localhost",~port=8000,
|
||||
~user="source",~password="hackme",
|
||||
~genre="Misc",~url="http://savonet.sf.net/",
|
||||
~name="OCaml Radio!",~public=true, ~format="",
|
||||
~dumpfile="", ~icy_metadata="guess",
|
||||
~on_connect={()}, ~on_disconnect={()},
|
||||
~aim="",~icq="",~irc="",~icy_reset=true,
|
||||
~fallible=false,~on_start={()},~on_stop={()},
|
||||
e,s)
|
||||
=
|
||||
(pair,42)
|
||||
end
|
||||
|
||||
def output.shoutcast(
|
||||
~id="output.shoutcast",~start=true,
|
||||
~restart=false,~restart_delay=3,
|
||||
~host="localhost",~port=8000,
|
||||
~user="source",~password="hackme",
|
||||
~genre="Misc",~url="http://savonet.sf.net/",
|
||||
~name="OCaml Radio!",~public=true, ~format="",
|
||||
~dumpfile="", ~icy_metadata="guess",
|
||||
~on_connect={()}, ~on_disconnect={()},
|
||||
~aim="",~icq="",~irc="",~icy_reset=true,
|
||||
~fallible=false,~on_start={()},~on_stop={()},
|
||||
e,s)
|
||||
=
|
||||
(blank() : source(3,0,0))
|
||||
end
|
||||
|
||||
x = (output.shoutcast,blank())
|
|
@ -97,41 +97,63 @@ def merge_tracks(s)
|
|||
sequence(merge=true,[s])
|
||||
end
|
||||
|
||||
# Default inputs and outpus
|
||||
#
|
||||
# They are called "prefered" but it's not a user preference,
|
||||
# just a view of what's generally preferable among the available
|
||||
# modules.
|
||||
# It is important that input and output preferences are in the
|
||||
# same order: the chosen I/O should work in the same clock, we don't
|
||||
# want an ALSA input and OSS output. The only exception is AO:
|
||||
# it is the default output after dummy, so the input will be a dummy
|
||||
# when AO is used for output.
|
||||
|
||||
output.prefered=output.dummy
|
||||
%ifdef output.oss
|
||||
output.prefered=output.oss
|
||||
%ifdef output.ao
|
||||
output.prefered=output.ao
|
||||
%endif
|
||||
%ifdef output.alsa
|
||||
output.prefered=output.alsa
|
||||
%endif
|
||||
%ifdef output.oss
|
||||
output.prefered=output.oss
|
||||
%endif
|
||||
%ifdef output.portaudio
|
||||
output.prefered = output.portaudio
|
||||
%endif
|
||||
%ifdef output.pulseaudio
|
||||
output.prefered=output.pulseaudio
|
||||
%endif
|
||||
%ifdef output.ao
|
||||
output.prefered=output.ao
|
||||
%endif
|
||||
# Output to local audio card using the first available driver in this list:
|
||||
# ao, pulseaudio, alsa, oss, dummy
|
||||
# Output to local audio card using the first available driver in
|
||||
# pulseaudio, portaudio, oss, alsa, ao, dummy.
|
||||
# @category Source / Output
|
||||
def output.prefered(~id="",s)
|
||||
output.prefered(id=id,s)
|
||||
def output.prefered(~id="",~fallible=false,
|
||||
~on_start={()},~on_stop={()},~start=true,s)
|
||||
output.prefered(id=id,fallible=fallible,
|
||||
start=start,on_start=on_start,on_stop=on_stop,
|
||||
s)
|
||||
end
|
||||
|
||||
in = fun () -> blank()
|
||||
%ifdef input.oss
|
||||
in = fun () -> input.oss(id="oss_mic")
|
||||
%endif
|
||||
def in(~id="",~start=true,~on_start={()},~on_stop={()},~fallible=false)
|
||||
blank(id=id)
|
||||
end
|
||||
%ifdef input.alsa
|
||||
in = fun () -> input.alsa(id="alsa_mic")
|
||||
in = input.alsa
|
||||
%endif
|
||||
%ifdef input.oss
|
||||
in = input.oss
|
||||
%endif
|
||||
%ifdef input.portaudio
|
||||
in = fun () -> input.portaudio(id="pa_mic")
|
||||
in = input.portaudio
|
||||
%endif
|
||||
# Create a source from the first available input driver in this list:
|
||||
# portaudio, alsa, oss, blank.
|
||||
%ifdef input.pulseaudio
|
||||
in = input.pulseaudio
|
||||
%endif
|
||||
# Create a source from the first available input driver in
|
||||
# pulseaudio, portaudio, oss, alsa, blank.
|
||||
# @category Source / Input
|
||||
def in()
|
||||
in()
|
||||
def in(~id="",~start=true,~on_start={()},~on_stop={()},~fallible=false)
|
||||
in(id=id,start=start,on_start=on_start,on_stop=on_stop,fallible=fallible)
|
||||
end
|
||||
|
||||
# Output a stream using the 'output.prefered' operator. The input source does
|
||||
|
@ -362,6 +384,11 @@ def read(~hide=false)
|
|||
s
|
||||
end
|
||||
|
||||
file.mime_default = fun (_) -> ""
|
||||
%ifdef file.mime
|
||||
file.mime_default = file.mime
|
||||
%endif
|
||||
|
||||
# Generic mime test. First try to use file.mime if it exist.
|
||||
# Otherwise try to get the value using the file binary.
|
||||
# Returns "" (empty string) if no value can be find.
|
||||
|
@ -376,15 +403,8 @@ def get_mime(file) =
|
|||
""
|
||||
end
|
||||
end
|
||||
def mime_method(file) =
|
||||
ret = ""
|
||||
%ifdef file.mime
|
||||
ret = file.mime(file)
|
||||
%endif
|
||||
ret
|
||||
end
|
||||
# First try mime method
|
||||
ret = mime_method(file)
|
||||
ret = file.mime_default(file)
|
||||
if ret != "" then
|
||||
ret
|
||||
else
|
||||
|
@ -403,8 +423,8 @@ end
|
|||
|
||||
# Creates a source that fails to produce anything.
|
||||
# @category Source / Input
|
||||
def fail()
|
||||
fallback([])
|
||||
def fail(~id="")
|
||||
fallback(id=id,[])
|
||||
end
|
||||
|
||||
# Creates a source that plays only one track of the input source.
|
||||
|
@ -677,6 +697,17 @@ def enable_replaygain_metadata(
|
|||
add_metadata_resolver("replay_gain", replaygain_metadata)
|
||||
end
|
||||
|
||||
# Assign a new clock to the given source (and to other time-dependent
|
||||
# sources) and return the source. It is a conveniency wrapper around
|
||||
# clock.assign_new(), allowing more concise scripts in some cases.
|
||||
# @category Liquidsoap
|
||||
# @param ~sync Do not synchronize the clock on regular wallclock time, \
|
||||
# but try to run as fast as possible (CPU burning mode).
|
||||
def clock(~sync=true,~id="",s)
|
||||
clock.assign_new(sync=sync,id=id,[s])
|
||||
s
|
||||
end
|
||||
|
||||
# Create a log of clock times for all the clocks initially present.
|
||||
# The log is in a simple format which you can directly use with gnuplot.
|
||||
# @category Liquidsoap
|
||||
|
|
|
@ -14,7 +14,7 @@ output_shoutcast = false
|
|||
# Logging settings #
|
||||
###########################################
|
||||
log_file = "/var/log/airtime/pypo-liquidsoap/<script>.log"
|
||||
log_level = 3
|
||||
#log_level = 3
|
||||
|
||||
###########################################
|
||||
# Icecast Stream settings #
|
||||
|
|
|
@ -53,11 +53,11 @@ s = map_metadata(append_title, s)
|
|||
|
||||
|
||||
if output_sound_device then
|
||||
out_device = out(s)
|
||||
ignore(out(s))
|
||||
end
|
||||
|
||||
if output_icecast_mp3 then
|
||||
out_mp3 = output.icecast(%mp3,
|
||||
ignore(output.icecast(%mp3,
|
||||
host = icecast_host,
|
||||
port = icecast_port,
|
||||
password = icecast_pass,
|
||||
|
@ -69,11 +69,12 @@ if output_icecast_mp3 then
|
|||
description = icecast_description,
|
||||
genre = icecast_genre,
|
||||
s)
|
||||
)
|
||||
end
|
||||
|
||||
if output_icecast_vorbis then
|
||||
if output_icecast_vorbis_metadata then
|
||||
out_vorbis = output.icecast(%vorbis,
|
||||
ignore(output.icecast(%vorbis,
|
||||
host = icecast_host,
|
||||
port = icecast_port,
|
||||
password = icecast_pass,
|
||||
|
@ -85,11 +86,12 @@ if output_icecast_vorbis then
|
|||
description = icecast_description,
|
||||
genre = icecast_genre,
|
||||
s)
|
||||
)
|
||||
else
|
||||
#remove metadata from ogg source and merge tracks to fix bug
|
||||
#with vlc and mplayer disconnecting at the end of every track
|
||||
s = add(normalize=false, [amplify(0.00001, noise()),s])
|
||||
out_vorbis = output.icecast(%vorbis,
|
||||
ignore(output.icecast(%vorbis,
|
||||
host = icecast_host,
|
||||
port = icecast_port,
|
||||
password = icecast_pass,
|
||||
|
@ -101,11 +103,12 @@ if output_icecast_vorbis then
|
|||
description = icecast_description,
|
||||
genre = icecast_genre,
|
||||
s)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if output_shoutcast then
|
||||
out_shoutcast = output.shoutcast(%mp3,
|
||||
ignore(output.shoutcast(%mp3,
|
||||
host = shoutcast_host,
|
||||
port = shoutcast_port,
|
||||
password = shoutcast_pass,
|
||||
|
@ -115,5 +118,6 @@ if output_shoutcast then
|
|||
url = shoutcast_url,
|
||||
genre = shoutcast_genre,
|
||||
s)
|
||||
)
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue