Skip to content
Snippets Groups Projects
Commit 34e22493 authored by David Trattnig's avatar David Trattnig
Browse files

Configurable fallback for folder, playlist. #7

parent 2f1c7d26
No related branches found
No related tags found
No related merge requests found
......@@ -41,7 +41,7 @@ In certain scenarios like for development you might want to do a bare metal inst
## Quickstart
1. Create the folder structure `./audio/station/` in the project root and populate `station` with some music files. This folder is picked up as a so-called *Station Fallback* in case no other music is scheduled or if silence is detected.
1. Create the folder structure `./audio/fallback/` in the project root and populate `fallback` with some music files. This folder is picked up as a so-called *Station Fallback* in case no other music is scheduled or if silence is detected.
2. Execute `./run.sh` to get the *Engine Core* server running.
3. Voilá, you should hear some music!
......@@ -55,7 +55,7 @@ After this is working you might be ready for a more sophisticated setup of the e
The aformentioned `audio` folder is the base for retrieving audio files. Engine Core is referencing three folders from this so-called *Audio Store*:
- **`audio/station/`**: A local folder for any emergency playback, also called *Station Fallback*. All audio files inside are played in a randomized order, if no actually scheduled music is played by the engine. The folder is being watched for changes. So you can add/remove audio on the fly.
- **`audio/fallback/`**: A local folder for any emergency playback, also called *Station Fallback*. All audio files inside are played in a randomized order, if no actually scheduled music is played by the engine. The folder is being watched for changes. So you can add/remove audio on the fly.
- **`audio/playlist/`**: Put a file `station-fallback-playlist.m3u` in here and it has the same effect as the fallback folder. If nothing else is scheduled, the M3U Playlist is played with higher priority than the folder. The playlist is being watched for changes.
- **`audio/source/`**: This is the location for audio files provided by [Tank](https://gitlab.servus.at/aura/tank). Usually any audio files which are part of the scheduled programme are read for their broadcast from here. If you are running all AURA components on a single machine you should be fine with just creating a symbolic link to the relevant Tank folder (`ln -s ../engine/audio ./audio`). But in some [distributed and redundant production scenario](https://gitlab.servus.at/aura/meta/-/blob/master/docs/installation-guide.md) you might think about more advanced options on how to sync your audio files between machines. You can find some ideas in the doc " [Setting up the Audio Store](https://gitlab.servus.at/aura/meta/-/blob/master/docs/setup-audio-store.md)".
......
......@@ -21,10 +21,12 @@ audio_playlist_folder="../audio/playlist"
# Sets the time how long we have to fade in and out, when we select another mixer input values are in seconds
fade_in_time="1.5"
fade_out_time="1.5"
# Fallback type: "folder", "playlist" or "none"
fallback_type="folder"
# A playlist holding music for Station Fallbacks (optional)
fallback_music_playlist= "station-fallback-playlist.m3u"
# A folder holding music for Station Fallbacks (optional)
fallback_music_folder="../audio/station"
fallback_music_folder="../audio/fallback"
# The time in seconds how often the folder should be re-scanned
# Do not reload too often when using large folders
fallback_music_folder_reload="300"
......
......@@ -21,10 +21,12 @@ audio_playlist_folder="../audio/playlist"
# Sets the time how long we have to fade in and out, when we select another mixer input values are in seconds
fade_in_time="1.5"
fade_out_time="1.5"
# Fallback type: "folder", "playlist" or "none"
fallback_type="folder"
# A playlist holding music for Station Fallbacks (optional)
fallback_music_playlist= "station-fallback-playlist.m3u"
# A folder holding music for Station Fallbacks (optional)
fallback_music_folder="../audio/station"
fallback_music_folder="../audio/fallback"
# The time in seconds how often the folder should be re-scanned
# Do not reload too often when using large folders
fallback_music_folder_reload="300"
......
......@@ -21,10 +21,12 @@ audio_playlist_folder="../audio/playlist"
# Sets the time how long we have to fade in and out, when we select another mixer input values are in seconds
fade_in_time="1.5"
fade_out_time="1.5"
# Fallback type: "folder", "playlist" or "none"
fallback_type="folder"
# A playlist holding music for Station Fallbacks (optional)
fallback_music_playlist= "station-fallback-playlist.m3u"
# A folder holding music for Station Fallbacks (optional)
fallback_music_folder="../audio/station"
fallback_music_folder="../audio/fallback"
# The time in seconds how often the folder should be re-scanned
# Do not reload too often when using large folders
fallback_music_folder_reload="300"
......
#
# 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/>.
fallback_inputs = ref []
#####################################
# FALLBACK SOURCES #
#####################################
# Create Sources
input_fallback_scheduled_0 = request.equeue(id="in_fallback_scheduled_0")
input_fallback_scheduled_1 = request.equeue(id="in_fallback_scheduled_1")
# Apply ReplayGain Normalization
input_fallback_scheduled_0 = amplify(id="in_fallback_scheduled_0", 1., override="replay_gain", input_fallback_scheduled_0)
input_fallback_scheduled_1 = amplify(id="in_fallback_scheduled_1", 1., override="replay_gain", input_fallback_scheduled_1)
# Add Event Handlers
input_fallback_scheduled_0 = on_metadata(id="in_fallback_scheduled_0", on_metadata_notification, input_fallback_scheduled_0)
input_fallback_scheduled_1 = on_metadata(id="in_fallback_scheduled_1", on_metadata_notification, input_fallback_scheduled_1)
# Mixer for more control of scheduled fallbacks
fallback_mixer = mix(id="mixer_fallback",
list.append(
[
input_fallback_scheduled_0,
input_fallback_scheduled_1
],
!fallback_inputs
)
)
stripped_fallback_mixer = strip_blank(
id="fallback_strip_blank",
track_sensitive=false,
max_blank=fallback_max_blank,
min_noise=fallback_min_noise,
threshold=fallback_threshold,
fallback_mixer
)
#####################################
# SERVER FUNCTIONS #
#####################################
# Clear Fallback Queue A
server.register(namespace=source.id(input_fallback_scheduled_0),
description="Clear all items of the scheduled fallback queue A.",
usage="clear",
"clear",
fun (s) ->
begin
source.skip(input_fallback_scheduled_0)
clear_queue(input_fallback_scheduled_0)
"Clearing done."
end
)
# Clear Fallback Queue B
server.register(namespace=source.id(input_fallback_scheduled_1),
description="Clear all items of the scheduled fallback queue B.",
usage="clear",
"clear",
fun (s) ->
begin
source.skip(input_fallback_scheduled_1)
clear_queue(input_fallback_scheduled_1)
"Clearing done."
end
)
# Seek Fallback Queue A
server.register(namespace = source.id(input_fallback_scheduled_0),
description="Seek to relative position in #{source.id(input_fallback_scheduled_0)}",
usage = "seek <duration in seconds>",
"seek",
fun (t) ->
begin
log("Seeking #{t} sec")
t = float_of_string(default=0.,t)
ret = source.seek(input_fallback_scheduled_0, t)
"Seeked #{ret} seconds."
end
)
# Seek Fallback Queue B
server.register(namespace = source.id(input_fallback_scheduled_1),
description="Seek to relative position in #{source.id(input_fallback_scheduled_1)}",
usage = "seek <duration in seconds>",
"seek",
fun (t) ->
begin
log("Seeking #{t} sec")
t = float_of_string(default=0.,t)
ret = source.seek(input_fallback_scheduled_1, t)
"Seeked #{ret} seconds."
end
)
\ No newline at end of file
......@@ -23,6 +23,7 @@
icecast_vorbis_metadata = false
inputs = ref []
# Load settings from ini file
%include "settings.liq"
......@@ -67,14 +68,6 @@ end
#####################################
# When some regular playout is happening and it is returned to the fallback,
# we don't want to resume the previous fallback track:
def on_track_change(s) =
source.skip(station_playlist)
source.skip(station_folder)
end
mixer = mix(id="mixer",
list.append(
[
......@@ -98,38 +91,7 @@ stripped_stream = strip_blank(
mixer
)
stripped_stream = on_track(on_track_change, stripped_stream)
# Scheduled fallback is not used, since we replaced it with "default playlists":
# fallback_one = fallback(
# id="fallback-scheduled",
# track_sensitive=false,
# replay_metadata=true,
# [ stripped_stream, stripped_fallback_mixer])
# fallback_one = on_track(on_track_change, fallback_one)
fallback_station_playlist = fallback(
id="fallback-station-playlist",
track_sensitive=false,
replay_metadata=true,
[ stripped_stream, station_playlist])
station_folder = mksafe(
playlist(id="station_folder",
fallback_station_dir,
mode="randomize",
reload=fallback_station_dir_reload,
reload_mode="seconds"))
fallback_station_folder = fallback(
id="fallback-station-folder",
track_sensitive=false,
replay_metadata=true,
[ fallback_station_playlist, station_folder])
output_source = fallback_station_folder
output_source = attach_fallback_source(stripped_stream)
#####################################
# OUTPUTS #
......
......@@ -17,123 +17,81 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
fallback_inputs = ref []
#####################################
# FALLBACK SOURCES #
# SOURCES #
#####################################
fallback_folder = ref blank()
fallback_playlist = ref blank()
# Create Sources
input_fallback_scheduled_0 = request.equeue(id="in_fallback_scheduled_0")
input_fallback_scheduled_1 = request.equeue(id="in_fallback_scheduled_1")
station_playlist = playlist(
id="station_playlist",
fallback_station_playlist_path,
mode="randomize",
reload_mode="watch",
reload=0)
station_folder = mksafe(
playlist(id="station_folder",
fallback_station_dir,
mode="randomize",
reload=fallback_station_dir_reload,
reload_mode="seconds"))
# Apply ReplayGain Normalization
input_fallback_scheduled_0 = amplify(id="in_fallback_scheduled_0", 1., override="replay_gain", input_fallback_scheduled_0)
input_fallback_scheduled_1 = amplify(id="in_fallback_scheduled_1", 1., override="replay_gain", input_fallback_scheduled_1)
station_playlist = amplify(id="station_playlist", 1., override="replay_gain", station_playlist)
station_folder = amplify(id="station_folder", 1., override="replay_gain", station_folder)
# Add Event Handlers
input_fallback_scheduled_0 = on_metadata(id="in_fallback_scheduled_0", on_metadata_notification, input_fallback_scheduled_0)
input_fallback_scheduled_1 = on_metadata(id="in_fallback_scheduled_1", on_metadata_notification, input_fallback_scheduled_1)
station_playlist = on_metadata(id="station_playlist", on_metadata_notification, station_playlist)
station_folder = on_metadata(id="station_folder", on_metadata_notification, station_folder)
# Mixer for more control of scheduled fallbacks
fallback_mixer = mix(id="mixer_fallback",
list.append(
[
input_fallback_scheduled_0,
input_fallback_scheduled_1
],
!fallback_inputs
)
)
stripped_fallback_mixer = strip_blank(
id="fallback_strip_blank",
track_sensitive=false,
max_blank=fallback_max_blank,
min_noise=fallback_min_noise,
threshold=fallback_threshold,
fallback_mixer
)
#####################################
# SERVER FUNCTIONS #
# EVENTS #
#####################################
# When some regular playout is happening and it is returned to the fallback,
# we don't want to resume the previous fallback track
def on_track_change(s) =
if fallback_type == "folder" then
log("Skipping track in fallback folder ...")
source.skip(!fallback_folder)
elsif fallback_type == "playlist" then
log("Skipping track in fallback playlist ...")
source.skip(!fallback_playlist)
end
end
# Clear Fallback Queue A
server.register(namespace=source.id(input_fallback_scheduled_0),
description="Clear all items of the scheduled fallback queue A.",
usage="clear",
"clear",
fun (s) ->
begin
source.skip(input_fallback_scheduled_0)
clear_queue(input_fallback_scheduled_0)
"Clearing done."
end
)
# Clear Fallback Queue B
server.register(namespace=source.id(input_fallback_scheduled_1),
description="Clear all items of the scheduled fallback queue B.",
usage="clear",
"clear",
fun (s) ->
begin
source.skip(input_fallback_scheduled_1)
clear_queue(input_fallback_scheduled_1)
"Clearing done."
end
)
# Seek Fallback Queue A
server.register(namespace = source.id(input_fallback_scheduled_0),
description="Seek to relative position in #{source.id(input_fallback_scheduled_0)}",
usage = "seek <duration in seconds>",
"seek",
fun (t) ->
begin
log("Seeking #{t} sec")
t = float_of_string(default=0.,t)
ret = source.seek(input_fallback_scheduled_0, t)
"Seeked #{ret} seconds."
end
)
# Seek Fallback Queue B
server.register(namespace = source.id(input_fallback_scheduled_1),
description="Seek to relative position in #{source.id(input_fallback_scheduled_1)}",
usage = "seek <duration in seconds>",
"seek",
fun (t) ->
begin
log("Seeking #{t} sec")
t = float_of_string(default=0.,t)
ret = source.seek(input_fallback_scheduled_1, t)
"Seeked #{ret} seconds."
#####################################
# FUNCTIONS #
#####################################
def attach_fallback_source(main_stream)
all_sources = if fallback_type == "folder" then
log("Fallback Type: FOLDER")
s = playlist(id="fallback_folder",
fallback_station_dir,
mode="randomize",
reload=fallback_station_dir_reload,
reload_mode="seconds")
s = amplify(id="fallback_folder", 1., override="replay_gain", s)
s = on_metadata(id="fallback_playlist", on_metadata_notification, s)
s = mksafe(s)
fallback_folder := s
[ on_track(on_track_change, main_stream), !fallback_folder ]
elsif fallback_type == "playlist" then
log("Fallback Type: PLAYLIST")
s = playlist(
id="fallback_playlist",
fallback_station_playlist_path,
mode="randomize",
reload_mode="watch",
reload=0)
s = amplify(id="fallback_playlist", 1., override="replay_gain", s)
s = on_metadata(id="fallback_playlist", on_metadata_notification, s)
s = mksafe(s)
fallback_playlist := s
[ on_track(on_track_change, main_stream), !fallback_playlist ]
else
log("Fallback Type: NONE")
[ mksafe(main_stream) ]
end
)
\ No newline at end of file
fallback(
id="fallback-source",
track_sensitive=false,
replay_metadata=true,
all_sources)
end
......@@ -54,9 +54,10 @@ set("server.socket.path", "#{socket_dir}/<script>.sock")
engine_control = list.assoc(default="localhost:1337", "engine_control_host", ini)
# SOUND CARD SETTINGS
set("audio.converter.samplerate.converters",["libsamplerate","native"])
# set("audio.converter.samplerate.converters",["libsamplerate","native"])
# set("audio.converter.samplerate.converters",["ffmpeg","libsamplerate","native"])
set("decoder.file_decoders",["META","WAV","AIFF","FLAC","AAC","MP4","OGG","MAD"])
#print(ini)
a0_in = list.assoc(default="", "input_device_0", ini)
a1_in = list.assoc(default="", "input_device_1", ini)
......@@ -76,6 +77,8 @@ audio_playlist_folder = "#{engine_config_folder}/playlists"
audio_playlist_folder = list.assoc(default=audio_playlist_folder, "audio_playlist_folder", ini)
# FALLBACK SETTINGS
fallback_type = list.assoc(default="folder", "fallback_type", ini)
fallback_station_playlist_name = "station-fallback-playlist.m3u"
fallback_station_playlist_name = list.assoc(default=fallback_station_playlist_name, "fallback_music_playlist", ini)
fallback_station_playlist_path = "#{audio_playlist_folder}/#{fallback_station_playlist_name}"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment