Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optionally persist cross/fade overrides, add tests #2499

Merged
merged 3 commits into from
Jul 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ Changes:
- Added support for a Javascript build an interpreter.
- Removed support for `%define` variables, superseeded by support for actual
variables in encoders.
- Switched default persistence for cross and fade-related overrides
to follow documented behavior. By default, `"liq_fade_out"`, `"liq_fade_skip"`,
`"liq_fade_in"`, `"liq_cross_duration"` and `"liq_fade_type"` now all reset on
new tracks. Use `persist_overrides` to revert to previous behavior
(`persist_override` for `cross`/`crossfade`) (#2488).

---
2.0.6 (2022-06-20)
Expand Down
17 changes: 17 additions & 0 deletions doc/content/migrating.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@ Migrating to a new Liquidsoap version
In this page, we list the most common catches when migrating to a new version of
Liquidsoap.

From 2.1.x to 2.2.x
-------------------

### Metadata overrides

Some metadata overrides have been made to reset on track boundaries. Previously, those were permanent even though they
were documented as only applying to the current track. If you need to keep the previous behavior, you can used the
`persist_overrides` parameters (`persis_override` for `cross`/`crossfade`).

The list of concerned metadata is:

* `"liq_fade_out"`
* `"liq_fade_skip"`
* `"liq_fade_in"`
* `"liq_cross_duration"`
* `"liq_fade_type"`

From 2.0.x to 2.1.x
-------------------

Expand Down
34 changes: 24 additions & 10 deletions src/core/operators/cross.ml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ let finalise_child_clock child_clock source =
(** [rms_width] and [minimum_length] are all in samples.
* [cross_length] is in ticks (like #remaining estimations).
* We are assuming a fixed audio kind -- at least for now. *)
class cross ~kind val_source ~duration_getter ~override_duration ~rms_width
~minimum_length ~conservative ~active transition =
class cross ~kind val_source ~duration_getter ~override_duration
~persist_override ~rms_width ~minimum_length ~conservative ~active transition
=
let s = Lang.to_source val_source in
let original_duration_getter = duration_getter in
object (self)
inherit source ~name:"cross" kind as super

Expand All @@ -49,10 +51,11 @@ class cross ~kind val_source ~duration_getter ~override_duration ~rms_width
* Going with the same choice as above for now. *)
method self_sync = s#self_sync
val mutable cross_length = 0
val mutable duration_getter = duration_getter
val mutable duration_getter = original_duration_getter
method cross_duration = duration_getter ()

method set_cross_length =
let new_cross_length = duration_getter () in
let new_cross_length = self#cross_duration in
let main_new_cross_length = Frame.main_of_seconds new_cross_length in

if main_new_cross_length <> cross_length then
Expand Down Expand Up @@ -182,6 +185,8 @@ class cross ~kind val_source ~duration_getter ~override_duration ~rms_width
| _ -> ()

method private update_cross_length frame pos =
if Frame.is_partial frame && not persist_override then
duration_getter <- original_duration_getter;
List.iter
(fun (p, m) ->
if p >= pos then (
Expand Down Expand Up @@ -439,6 +444,10 @@ let () =
Some
"Metadata field which, if present and containing a float, overrides \
the 'duration' parameter for current track." );
( "persist_override",
Lang.bool_t,
Some (Lang.bool false),
Some "Keep duration override on track change." );
( "minimum",
Lang.float_t,
Some (Lang.float (-1.)),
Expand Down Expand Up @@ -481,6 +490,13 @@ let () =
("", Lang.source_t k, None, None);
]
~return_t:k ~category:`Audio
~meth:
[
( "cross_duration",
Lang.([], fun_t [] float_t),
"Get the current crossfade duration.",
fun s -> Lang.val_fun [] (fun _ -> Lang.float s#cross_duration) );
]
~descr:
"Cross operator, allowing the composition of the _n_ last seconds of a \
track with the beginning of the next track, using a transition function \
Expand All @@ -491,6 +507,7 @@ let () =
let override_duration =
Lang.to_string (List.assoc "override_duration" p)
in
let persist_override = Lang.to_bool (List.assoc "persist_override" p) in
let minimum = Lang.to_float (List.assoc "minimum" p) in
let minimum_length = Frame.audio_of_seconds minimum in
let rms_width = Lang.to_float (List.assoc "width" p) in
Expand All @@ -500,9 +517,6 @@ let () =
let active = Lang.to_bool (List.assoc "active" p) in
let source = Lang.assoc "" 2 p in
let kind = Kind.of_kind kind in
let c =
new cross
~kind source transition ~conservative ~active ~duration_getter
~rms_width ~minimum_length ~override_duration
in
(c :> source))
new cross
~kind source transition ~conservative ~active ~duration_getter
~rms_width ~minimum_length ~override_duration ~persist_override)
80 changes: 60 additions & 20 deletions src/libs/fades.liq
Original file line number Diff line number Diff line change
Expand Up @@ -81,27 +81,31 @@ end
# @category Source / Sound Processing
# @param ~id Force the value of the source ID.
# @param ~duration Duration of the fading. This value can be set on a per-file basis using the metadata field passed as override.
# @param ~override_duration Metadata field which, if present and containing a float, overrides the 'duration' parameter for current track.
# @param ~override_type Metadata field which, if present and correct, overrides the 'type' parameter for current track.
# @param ~override_duration Metadata field which, if present and containing a float, overrides the 'duration' parameter for the current track.
# @param ~override_type Metadata field which, if present and correct, overrides the 'type' parameter for the current track.
# @param ~persist_overrides Keep duration and type overrides on track change.
# @param ~track_sensitive Be track sensitive (if `false` we only fade ou once at the beginning of the track).
# @param ~type Fader shape (lin|sin|log|exp): linear, sinusoidal, logarithmic or exponential.
def fade.out(~id="fade.out",~duration=3.,
~override_duration="liq_fade_out",
~override_type="liq_fade_type",
~persist_overrides=false,
~track_sensitive=true,
~type="lin",s) =
def log(x) = log(label=source.id(s),level=4,x) end
fn = ref(fun () -> 1.)
original_type = type
type = ref(type)
original_duration = duration
duration = ref(duration)
start_time = ref(-1.)
started = ref(false)

def start_fade(d,_) =
log("Fading out with #{d}s remaining.")
log("Fading out with type #{!type}, duration: #{!duration} and #{d}s remaining.")
start_time := source.time(s)
duration = if d < !duration then d else !duration end
fn := mkfade(start=1.,stop=0.,type=!type,duration=duration,s)
d = if d < !duration then d else !duration end
fn := mkfade(start=1.,stop=0.,type=!type,duration=d,s)
started := true
end

Expand Down Expand Up @@ -129,35 +133,48 @@ def fade.out(~id="fade.out",~duration=3.,
end
end

def reset_overrides(_) =
if not persist_overrides then
duration := original_duration
type := original_type
end
end

s = source.on_track(s, reset_overrides)
s = source.on_metadata(s, update_fade)
delay = fun () -> !duration
s =
if track_sensitive then
source.on_track(s, stop_fade)
else
start_fade(!duration, [])
s
end

delay = fun () -> !duration
s = if track_sensitive then source.on_end(s, delay=delay, start_fade) else s end
fade.scale(id=id, apply, s)
fade.scale(id=id, apply, s).{fade_duration = {!duration}, fade_type = {!type}}
end

# Fade when the metadata trigger is received and then skip.
# @category Source / Sound Processing
# @param ~id Force the value of the source ID.
# @param ~duration Duration of the fading. This value can be set on a per-file basis using the metadata field passed as override.
# @param ~override_duration Metadata field which, if present and containing a float, overrides the 'duration' parameter for current track.
# @param ~override_type Metadata field which, if present and correct, overrides the 'type' parameter for current track.
# @param ~override_duration Metadata field which, if present and containing a float, overrides the 'duration' parameter for the current track.
# @param ~override_type Metadata field which, if present and correct, overrides the 'type' parameter for the current track.
# @param ~override_skip Metadata field which, when present and set to "true", will trigger the fade
# @param ~persist_overrides Keep duration and type overrides on track change.
# @param ~type Fader shape (lin|sin|log|exp): linear, sinusoidal, logarithmic or exponential.
def fade.skip(~id="fade.skip",~duration=5.,
~override_duration="liq_fade_skip",
~override_type="liq_fade_skip",
~override_type="liq_fade_type",
~persist_overrides=false,
~override_skip="liq_skip_meta",
~type="lin",s) =
def log(x) = log(label=source.id(s),level=4,x) end
fn = ref(fun () -> 1.)
original_type = type
type = ref(type)
original_duration = duration
duration = ref(duration)

def apply() =
Expand Down Expand Up @@ -193,27 +210,39 @@ def fade.skip(~id="fade.skip",~duration=5.,
end
end

def reset_overrides(_) =
if not persist_overrides then
duration := original_duration
type := original_type
end
end

s = source.on_track(s, reset_overrides)
s = source.on_metadata(s, update_fade)
s = source.on_track(s, stop_fade)
fade.scale(id=id, apply, s)
fade.scale(id=id, apply, s).{fade_duration = {!duration}, fade_type = {!type} }
end

# Fade the beginning of tracks.
# @category Source / Sound Processing
# @param ~id Force the value of the source ID.
# @param ~duration Duration of the fading. This value can be set on a per-file basis using the metadata field passed as override.
# @param ~override_duration Metadata field which, if present and containing a float, overrides the 'duration' parameter for current track.
# @param ~override_type Metadata field which, if present and correct, overrides the 'type' parameter for current track.
# @param ~override_duration Metadata field which, if present and containing a float, overrides the 'duration' parameter for the current track.
# @param ~override_type Metadata field which, if present and correct, overrides the 'type' parameter for the current track.
# @param ~persist_overrides Keep duration and type overrides on track change.
# @param ~track_sensitive Be track sensitive (if `false` we only fade in once at the beginning of the track).
# @param ~type Fader shape (lin|sin|log|exp): linear, sinusoidal, logarithmic or exponential.
def fade.in(~id="fade.in",~duration=3.,
~override_duration="liq_fade_in",
~override_type="liq_fade_type",
~persist_overrides=false,
~track_sensitive=true,
~type="lin",s) =
def log(x) = log(label=source.id(s),level=4,x) end
fn = ref(fun () -> 0.)
original_duration = duration
duration = ref(duration)
original_type = type
type = ref(type)

def apply() =
Expand All @@ -222,7 +251,7 @@ def fade.in(~id="fade.in",~duration=3.,
end

def start_fade(_) =
log("Fading in (type: #{!type}, duration: #{!duration}s).")
log("Fading in with type: #{!type} and duration: #{!duration}s.")
fn := mkfade(start=0.,stop=1.,type=!type,duration=!duration,s)
end

Expand All @@ -238,10 +267,18 @@ def fade.in(~id="fade.in",~duration=3.,
end
end

def reset_overrides(_) =
if not persist_overrides then
duration := original_duration
type := original_type
end
end

s = source.on_track(s, reset_overrides)
s = source.on_metadata(s, update_fade)
s = if track_sensitive then source.on_track(s, start_fade) else start_fade([]); s end

fade.scale(id=id,apply,s)
fade.scale(id=id, apply, s).{fade_duration = {!duration}, fade_type = {!type} }
end

# Simple transition for crossfade
Expand Down Expand Up @@ -342,6 +379,7 @@ end
# Metadata field which, if present and containing a \
# float, overrides the 'duration' parameter for current \
# track.
# @param ~persist_override Keep duration override on track change.
# @param ~fade_in Fade-in duration, if any.
# @param ~fade_out Fade-out duration, if any.
# @param ~width Width of the volume analysis window.
Expand All @@ -363,7 +401,7 @@ end
# removes duplicate metadata from the returned source.
# @param s The input source.
def crossfade(~id=null(), ~duration=5.,~override_duration="liq_cross_duration",
~fade_in=3.,~fade_out=3.,~smart=false,
~persist_override=false,~fade_in=3.,~fade_out=3.,~smart=false,
~default=(fun (a,b) -> (sequence([a, b]):source)),
~high=-15., ~medium=-32., ~margin=4., ~deduplicate=true,
~minimum=(-1.),~width=2.,~conservative=true,s)
Expand Down Expand Up @@ -395,14 +433,16 @@ def crossfade(~id=null(), ~duration=5.,~override_duration="liq_cross_duration",

let (cross_id, deduplicate_id) = deduplicate ? (null(), null(id)) : (null(id), null())

s = cross(id=cross_id, width=width, duration=duration, override_duration=override_duration,
crossed = cross(id=cross_id, width=width, duration=duration, persist_override=persist_override, override_duration=override_duration,
conservative=conservative, minimum=minimum, transition, s)

if deduplicate then
metadata.deduplicate(id=deduplicate_id, s)
s = if deduplicate then
metadata.deduplicate(id=deduplicate_id, crossed)
else
s
crossed
end

s.{cross_duration={crossed.cross_duration()}}
end

# Mixes two streams, with faded transitions between the state when only the
Expand Down
Loading