Introduction
You can use external programs in liquidsoap to decode audio files.
Basic operators
External decoders are registered using decoder.add`.
decoder.add(name="my_decoder",description="My custom decoder",
file_extensions=["foo"], decoder)
file_extensions
is a list of file extensions that the decoder can handle.decoder
is a function used to return the decoded file.- You can also use the
mimes
argument to file files based on their mime-type.
The decoder
function has a similar signature as protocol
resolution functions. This is because file decoding happen as part of
the protocol resolution. It takes a rlog
function, a
maxtime
maximum execution time stamp and an input file and
returns a decoded URI.
Decoded URI can be any url to pass down to the protocol resolution
pipeline. Most of the time, it should be a decoded file but it could
also be a annotate
uri if you wish to also pass down
decoded metadata along with the decoded file.
options = ""
# BEGIN
decoder.add(
name="OPENMPT123",
description=
"Decode files using the openmpt123 decoder binary",
mimes=
["audio/it",
"audio/xm",
"audio/s3m",
"audio/x-mod",
"audio/mod",
"audio/module-xm",
"audio/x-mod",
"application/playerpro",
"audio/x-s3m",
"application/soundapp",
"audio/med",
"audio/x-xm"
],file_extensions=
["xm",
"mtm",
"amf",
"stm",
"ult",
"wow",
"dmf",
"it",
"s3m",
"far",
"mod",
"mt2",
"okt",
"med",
"669"
],fun (~rlog, ~maxtime:_, infile) ->
begin
ret =
process.read.lines(
"openmpt123 --info #{process.quote(infile)} 2>&1"
)def get_meta(l, s) =
ret = string.extract(pattern="^(\\w+).+:\\s(.+)$", s)
if
list.length(ret) > 2
then
label = ret[1]
val = ret[2]
label = "openmpt:#{string.case(lower=true, label)}"
"#{string.quote(label)}=#{string.quote(val)}", ...l]
[else
lend
end
meta = list.fold(get_meta, [], ret)
prefix =
if
meta == []
then
""
else
"annotate:#{string.concat(separator=',', meta)}:"
end
# File is cleaned up as part of the request workflow.
outfile = file.temp(cleanup=false, "openmpt", ".wav")
try
let {status = {code}} =
process.run(
"openmpt123 --assume-terminal --quiet --force #{options} --output #{
process.quote(outfile)
} #{process.quote(infile)}"
)code == 0 ? "#{prefix}#{outfile}" : null
catch err do
file.remove(outfile)
rlog(
"Error while decoding #{infile} using ffmpeg: #{err}"
)
nullend
end
)