Commit a00c56e1 authored by David Trattnig's avatar David Trattnig
Browse files

Cleanup. #72

parent 0ce48e1e
......@@ -8,7 +8,7 @@ scripts/.engine.install-db.lock
.engine.install-db.lock
config/systemd/dev/
env.list
audio/source
audio
config/docker/engine.ini
src/liquidsoap/engine.sock
python
......
......@@ -5,17 +5,10 @@ stages:
before_script:
- apt-get -qq update
- apt-cache search libmariadb
- apt-get install -y python3-virtualenv virtualenv opam libev4 libev-dev libsndfile1 quelcom # mariadb-server libmariadbclient-dev
- /usr/bin/virtualenv venv
- . venv/bin/activate
- python3 -V
- apt-get install -y python3-virtualenv virtualenv
- /usr/bin/virtualenv python
- . python/bin/activate
- pip3 install -r requirements.txt
- mkdir /etc/aura
- mkdir /var/log/aura
- mkdir -p /opt/aura/engine/src/liquidsoap
- mkdir -p /opt/aura/engine/logs
- pwd
- cp config/sample-production.engine.ini config/engine.ini
run_test_cases:
......
......@@ -4,5 +4,4 @@ Flask==1.1.2
Flask_SQLAlchemy==2.4.3
mysqlclient==1.3.12
validators==0.12.1
accessify==0.3.1
http-parser==0.9.0
\ No newline at end of file
#!/bin/bash
opam update -y
opam init -y
opam switch create 4.08.0
opam install depext -y
opam depext taglib mad lame vorbis flac opus cry samplerate pulseaudio bjack alsa ssl liquidsoap -y
opam install taglib mad lame vorbis flac opus cry samplerate pulseaudio bjack alsa ssl liquidsoap -y
eval $(opam env)
#
# Aura Engine (https://gitlab.servus.at/aura/engine)
#
# Copyright (C) 2017-2020 - The Aura Engine Team.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Crossfade between tracks,
# taking the respective volume levels
# into account in the choice of the
# transition.
# @category Source / Track Processing
# @param ~start_next Crossing duration, if any.
# @param ~fade_in Fade-in duration, if any.
# @param ~fade_out Fade-out duration, if any.
# @param ~width Width of the volume analysis window.
# @param ~conservative Always prepare for
# a premature end-of-track.
# @param s The input source.
def crossfade (~start_next=5.,~fade_in=3.,
~fade_out=3., ~width=2.,
~conservative=false,s)
high = -20.
medium = -32.
margin = 4.
fade.out = fade.out(type="sin",duration=fade_out)
fade.in = fade.in(type="sin",duration=fade_in)
add = fun (a,b) -> add(normalize=false,[b,a])
log = log(label="crossfade")
def transition(a,b,ma,mb,sa,sb)
list.iter(fun(x)->
log(level=4,"Before: #{x}"),ma)
list.iter(fun(x)->
log(level=4,"After : #{x}"),mb)
if
# If A and B and not too loud and close,
# fully cross-fade them.
a <= medium and
b <= medium and
abs(a - b) <= margin
then
log("Transition: crossed, fade-in, fade-out.")
add(fade.out(sa),fade.in(sb))
elsif
# If B is significantly louder than A,
# only fade-out A.
# We don't want to fade almost silent things,
# ask for >medium.
b >= a + margin and a >= medium and b <= high
then
log("Transition: crossed, fade-out.")
add(fade.out(sa),sb)
elsif
# Do not fade if it's already very low.
b >= a + margin and a <= medium and b <= high
then
log("Transition: crossed, no fade-out.")
add(sa,sb)
elsif
# Opposite as the previous one.
a >= b + margin and b >= medium and a <= high
then
log("Transition: crossed, fade-in.")
add(sa,fade.in(sb))
# What to do with a loud end and
# a quiet beginning ?
# A good idea is to use a jingle to separate
# the two tracks, but that's another story.
else
# Otherwise, A and B are just too loud
# to overlap nicely, or the difference
# between them is too large and
# overlapping would completely mask one
# of them.
log("No transition: just sequencing.")
sequence([sa, sb])
end
end
cross(width=width, duration=start_next,
conservative=conservative,
transition,s)
end
# Custom crossfade to deal with jingles.
# def smarter_crossfade (~start_next=5.,~fade_in=3.,~fade_out=3.,
# ~default=(fun (a,b) -> sequence([a, b])),
# ~high=-15., ~medium=-32., ~margin=4.,
# ~width=2.,~conservative=false,s)
# fade.out = fade.out(type="sin",duration=fade_out)
# fade.in = fade.in(type="sin",duration=fade_in)
# add = fun (a,b) -> add(normalize=false,[b, a])
# log = log(label="smarter_crossfade")
# def transition(a,b,ma,mb,sa,sb)
# list.iter(fun(x)-> log(level=4,"Before: #{x}"),ma)
# list.iter(fun(x)-> log(level=4,"After : #{x}"),mb)
# if ma["type"] == "jingles" or mb["type"] == "jingles" then
# log("Old or new file is a jingle: sequenced transition.")
# sequence([sa, sb])
# elsif
# # If A and B are not too loud and close, fully cross-fade them.
# a <= medium and b <= medium and abs(a - b) <= margin
# then
# log("Old <= medium, new <= medium and |old-new| <= margin.")
# log("Old and new source are not too loud and close.")
# log("Transition: crossed, fade-in, fade-out.")
# add(fade.out(sa),fade.in(sb))
# elsif
# # If B is significantly louder than A, only fade-out A.
# # We don't want to fade almost silent things, ask for >medium.
# b >= a + margin and a >= medium and b <= high
# then
# log("new >= old + margin, old >= medium and new <= high.")
# log("New source is significantly louder than old one.")
# log("Transition: crossed, fade-out.")
# add(fade.out(sa),sb)
# elsif
# # Opposite as the previous one.
# a >= b + margin and b >= medium and a <= high
# then
# log("old >= new + margin, new >= medium and old <= high")
# log("Old source is significantly louder than new one.")
# log("Transition: crossed, fade-in.")
# add(sa,fade.in(sb))
# elsif
# # Do not fade if it's already very low.
# b >= a + margin and a <= medium and b <= high
# then
# log("new >= old + margin, old <= medium and new <= high.")
# log("Do not fade if it's already very low.")
# log("Transition: crossed, no fade.")
# add(sa,sb)
# # What to do with a loud end and a quiet beginning ?
# # A good idea is to use a jingle to separate the two tracks,
# # but that's another story.
# else
# # Otherwise, A and B are just too loud to overlap nicely,
# # or the difference between them is too large and overlapping would
# # completely mask one of them.
# log("No transition: using default.")
# default(sa, sb)
# end
# end
# #smart_cross(width=width, duration=start_next, conservative=conservative, transition, s)
# smart_crossfade(duration=start_next, fade_in=fade_in, fade_out=fade_out, width=width, conservative=conservative, transition, s)
# end
# create a pool
def fallback_create(~skip=true, name, requestor)
log("Creating channel #{name}")
# Create the request.dynamic source
# Set conservative to true to queue several songs in advance
#source = request.dynamic(conservative=true, length=50., id="pool_"^name, requestor, timeout=60.)
source = request.dynamic(length=50., id="pool_"^name, requestor, timeout=60.)
# Apply normalization using replaygain information
source = amplify(1., override="replay_gain", source)
# Skip blank when asked to
source =
if skip then
skip_blank(max_blank=fallback_max_blank, min_noise=fallback_min_noise, threshold=fallback_threshold, source)
else
source
end
# Tell the system when a new track is played
def do_meta(meta) =
filename = meta["filename"]
# artist = meta["artist"]
# title = meta["title"]
system('#{list.assoc(default="", "install_dir", ini)}/guru.py --on_play "#{filename}"')
end
source = on_metadata(do_meta, source)
log("channel created")
# Finally apply a smart crossfading
#smarter_crossfade(source)
crossfade(source)
end
def create_dynamic_playlist(next)
request.create(list.hd(default="", next))
end
def create_playlist() =
log("requesting next song for PLAYLIST")
result = get_process_lines('#{list.assoc(default="", "install_dir", ini)}/guru.py --get-next-file-for "playlist" --quiet')
create_dynamic_playlist(result)
end
def create_station_fallback() =
result = get_process_lines('#{list.assoc(default="", "install_dir", ini)}/guru.py --get-next-file-for station --quiet')
log("next song for STATION fallback is: #{result}")
create_dynamic_playlist(result)
end
def create_show_fallback() =
result = get_process_lines('#{list.assoc(default="", "install_dir", ini)}/guru.py --get-next-file-for show --quiet')
log("next song for SHOW fallback is: #{result}")
create_dynamic_playlist(result)
end
def create_timeslot_fallback() =
result = get_process_lines('#{list.assoc(default="", "install_dir", ini)}/guru.py --get-next-file-for timeslot --quiet')
log("next song for TIMESLOT fallback is: #{result}")
create_dynamic_playlist(result)
end
# create fallbacks
timeslot_fallback = fallback_create(skip=true, "timeslot_fallback", create_timeslot_fallback)
station_fallback = fallback_create(skip=true, "station_fallback", create_station_fallback)
show_fallback = fallback_create(skip=true, "show_fallback", create_show_fallback)
\ No newline at end of file
#
# Aura Engine (https://gitlab.servus.at/aura/engine)
#
# Copyright (C) 2017-2020 - The Aura Engine Team.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
r0_enable = list.assoc(default="", "rec_0", ini) == "y"
r1_enable = list.assoc(default="", "rec_1", ini) == "y"
r2_enable = list.assoc(default="", "rec_2", ini) == "y"
r3_enable = list.assoc(default="", "rec_3", ini) == "y"
r4_enable = list.assoc(default="", "rec_4", ini) == "y"
r0_folder = list.assoc(default="", "rec_0_folder", ini)
r0_duration = int_of_string(list.assoc(default="", "rec_0_duration", ini))
r0_encoding = list.assoc(default="", "rec_0_encoding", ini)
r0_bitrate = int_of_string(list.assoc(default="", "rec_0_bitrate", ini))
r0_channels = list.assoc(default="", "rec_0_channels", ini)
r0_filenamepattern = r0_folder^"/%Y-%m-%d/%Y-%m-%d-%H-%M."^r0_encoding
r1_folder = list.assoc(default="", "rec_1_folder", ini)
r1_duration = int_of_string(list.assoc(default="", "rec_1_duration", ini))
r1_encoding = list.assoc(default="", "rec_1_encoding", ini)
r1_bitrate = int_of_string(list.assoc(default="", "rec_1_bitrate", ini))
r1_channels = list.assoc(default="", "rec_1_channels", ini)
r1_filenamepattern = r1_folder^"/%Y-%m-%d/%Y-%m-%d-%H-%M."^r1_encoding
r2_folder = list.assoc(default="", "rec_2_folder", ini)
r2_duration = int_of_string(list.assoc(default="", "rec_2_duration", ini))
r2_encoding = list.assoc(default="", "rec_2_encoding", ini)
r2_bitrate = int_of_string(list.assoc(default="", "rec_2_bitrate", ini))
r2_channels = list.assoc(default="", "rec_2_channels", ini)
r2_filenamepattern = r2_folder^"/%Y-%m-%d/%Y-%m-%d-%H-%M."^r2_encoding
r3_folder = list.assoc(default="", "rec_3_folder", ini)
r3_duration = int_of_string(list.assoc(default="", "rec_3_duration", ini))
r3_encoding = list.assoc(default="", "rec_3_encoding", ini)
r3_bitrate = int_of_string(list.assoc(default="", "rec_3_bitrate", ini))
r3_channels = list.assoc(default="", "rec_3_channels", ini)
r3_filenamepattern = r3_folder^"/%Y-%m-%d/%Y-%m-%d-%H-%M."^r3_encoding
r4_folder = list.assoc(default="", "rec_4_folder", ini)
r4_duration = int_of_string(list.assoc(default="", "rec_4_duration", ini))
r4_encoding = list.assoc(default="", "rec_4_encoding", ini)
r4_bitrate = int_of_string(list.assoc(default="", "rec_4_bitrate", ini))
r4_channels = list.assoc(default="", "rec_4_channels", ini)
r4_filenamepattern = r4_folder^"/%Y-%m-%d/%Y-%m-%d-%H-%M."^r4_encoding
r0_is_recording = ref false
r1_is_recording = ref false
r2_is_recording = ref false
r3_is_recording = ref false
r4_is_recording = ref false
if r0_enable == true then
# enable recording status for that recorder
server.register(namespace="out_filesystem_0", "recording", fun (s) -> begin if !r0_is_recording == false then "false" else "true" end end)
# start the recorder
start_recorder(r0_folder, r0_duration, r0_encoding, r0_bitrate, r0_channels, r0_filenamepattern, r0_is_recording, output_source, "0")
end
if r1_enable == true then
server.register(namespace="out_filesystem_1", "recording", fun (s) -> begin if !r1_is_recording == false then "false" else "true" end end)
start_recorder(r1_folder, r1_duration, r1_encoding, r1_bitrate, r1_channels, r1_filenamepattern, r1_is_recording, output_source, "1")
end
if r2_enable == true then
server.register(namespace="out_filesystem_2", "recording", fun (s) -> begin if !r2_is_recording == false then "false" else "true" end end)
start_recorder(r2_folder, r2_duration, r2_encoding, r2_bitrate, r2_channels, r2_filenamepattern, r2_is_recording, output_source, "2")
end
if r3_enable == true then
server.register(namespace="out_filesystem_3", "recording", fun (s) -> begin if !r3_is_recording == false then "false" else "true" end end)
start_recorder(r3_folder, r3_duration, r3_encoding, r3_bitrate, r3_channels, r3_filenamepattern, r3_is_recording, output_source, "3")
end
if r4_enable == true then
server.register(namespace="out_filesystem_4", "recording", fun (s) -> begin if !r4_is_recording == false then "false" else "true" end end)
start_recorder(r4_folder, r4_duration, r4_encoding, r4_bitrate, r4_channels, r4_filenamepattern, r4_is_recording, output_source, "4")
end
\ No newline at end of file
#
# Aura Engine (https://gitlab.servus.at/aura/engine)
#
# Copyright (C) 2017-2020 - The Aura Engine Team.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# %include "readini.liq"
# ini = read_ini("/etc/aura/engine.ini")
# %include "settings.liq"
# TELNET SETTINGS
# set("server.telnet", true)
# set("server.telnet.bind_addr", "0.0.0.0")
# set("server.telnet.port", 2345)
inst = if argv(1) != "" then string_of(argv(1)) else 'record' end
instance = ref inst
audiobase = if !instance == 'record' then list.assoc(default="", "audiobase", ini) else list.assoc(default="", "altaudiobase", ini) end
rec_filetype = list.assoc(default="", "rec_filetype", ini)
filenamepattern = ref audiobase^"/%Y-%m-%d/%Y-%m-%d-%H-%M.flac"
# Der aktuelle Dateiname für die Aufnahme
recordingfile = ref ""
# wir definieren eine Referenz für die Stop-Funktion, die aber bisher noch nichts tun kann
stop_f = ref (fun () -> ())
# bewahre uns davor, dass zweimal gleichzeitig die gleiche Date aufgenommen wird
is_record_active = ref false
# Stop dump - wir definieren die Funktion, die stop_f ausführt
def stop_dump() =
f = !stop_f
f ()
end
def on_start()
recordingfile := list.hd(default="", get_process_lines("date +#{!filenamepattern}"))
end
# Wav header fixen und ggf. die Aufzeichnung beenden
def on_close(filename)
# es darf wieder aufgenommen werden
is_record_active := false
# if list.assoc(default="", "rec_filetype", ini) == 'wav'
# # Korrekten WAV-Header schreiben
# system("qwavheaderdump -F #{filename}")
# Naechsten Dateinamen vormerken
recordingfile := list.hd(default="", get_process_lines("date +#{!filenamepattern}"))
end
# Der input wie oben definiert
def get_input()
output_source
# input.alsa()
end
def get_output()
input = get_input()
d = int_of_string(list.assoc(default="", "rec_duration", ini))
if rec_filetype == 'flac' then
log("output file type is FLAC")
output.file(
id="recorder",
%flac(samplerate=44100, channels=2, compression=5, bits_per_sample=16),
perm = 0o664,
on_start=on_start,
!filenamepattern,
on_close=on_close,
reopen_when={ if !instance == 'record' then int_of_float(gettimeofday()/60.) mod 30 == 0 else false end },
input
)
else
# record in WAV
log("output file type is WAV")
output.file(
id="recorder",
%wav(stereo=true, channels=2, samplesize=16, header=true),
perm = 0o664,
on_start=on_start,
!filenamepattern,
on_close=on_close,
reopen_when={ if !instance == 'record' then int_of_float(gettimeofday()/60.) mod d == 0 else false end },
input
)
end
end
# Funktion gibt Auskunft welches File aktuell ist und wieviel Prozent bereits aufgenommen werden
def currecording()
curfile = !recordingfile
if curfile != "" then
percent_done = default="", get_process_lines("echo $(($(stat -c%s "^curfile^")/3174777))"))
"#{curfile}, #{percent_done}%"
else
"Nothing is being recorded now"
end
end
#Funktion zum Start der Aufzeichnung
def start_dump() =
log('start dump')
# don't record twice is_record_active
if !is_record_active == false then
is_record_active := true
log('starting to record')
record = get_output()
log('record defined')
# Die Stopfunkton zeigt nun auf die Shutdown-Funktion der aktuellen Source
stop_f := fun () -> begin
log('stop recording')
# Aufnahme sauber beenden
ignore(server.execute('recorder.stop'))
# Source zerstören
source.shutdown(record)
# Variable zurücksetzen
recordingfile := ""
end
else
log("recorder already active")
end
end
# Der Server wird durch 3 Funktionen bereichert
# Der User darf die Aufzeichnung manuell starten
server.register(namespace="record",
description="Start recording.",
usage="start",
"start",
fun (s) -> begin start_dump() "OK" end)
# Der User darf die Aufzeichnung manuell stoppen
server.register(namespace="record",
description="Stop recording.",
usage="stop",
"stop",
fun (s) -> begin stop_dump() "OK" end)
if !instance != 'record' then
# Der User darf einen Dateinamen für die Aufnahme definieren
server.register(namespace="record",
description="Define filename for output.",
usage="setfilename",
"setfilename",
fun (s) -> begin filenamepattern := audiobase^"/"^string_of(s) "OK" end)
end
# Der User kann sich über den Fortschritt der Aufnahme informieren
server.register(namespace="record",
description="Show current file.",
usage="curfile",
"curfile",
fun (s) -> currecording() )
output.dummy(blank(id="serve"))
#
# Aura Engine (https://gitlab.servus.at/aura/engine)
#
# Copyright (C) 2017-2020 - The Aura Engine Team.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# define file name pattern
filenamepattern = ref audiobase^"/%Y-%m-%d/%Y-%m-%d-%H-%M.flac"
# Der aktuelle Dateiname für die Aufnahme
recordingfile = ref ""
# bewahre uns davor, dass zweimal gleichzeitig die gleiche Date aufgenommen wird
is_record_active = ref false
#wir definieren eine Referenz für die Stop-Funktion, die aber bisher noch nichts tun kann
stop_f = ref (fun () -> ())
def start_wav_output(recfile, filenamepattern, recorder_number)
duration = int_of_string(list.assoc(default="", "rec_"^recorder_number^"_duration", ini))
channels = int_of_string(list.assoc(default="", "rec_"^recorder_number^"_channels", ini))
samplesize = int_of_string(list.assoc(default="", "rec_"^recorder_number^"_samplesize", ini))
ignore(duration)
ignore(channels)
ignore(samplesize)
# def on_start()
# recfile := list.hd(default="", get_process_lines("date +#{!filenamepattern}"))
# end
# def on_close(filename)
# recordingfile := list.hd(default="", get_process_lines("date +#{!filenamepattern}"))
# end
print(channels)
print(samplesize)
#out_wav = output.file(id="recorder", perm = 0o664, on_start=on_start, on_close=on_close, reopen_when={ int_of_float(gettimeofday()/60.) mod duration == 0 })
#out_wav(%wav(stereo=true, channels=2, samplesize=8, header=true, !filenamepattern, output_source)
#ignore(out_wav)