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

Add dB_levels to blank sources #3791

Merged
merged 1 commit into from
Mar 8, 2024
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
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ New:

- Added various ffmpeg timestamps when exporting ffmpeg metadata from filters.
- Added `enable_autocue_metadata` and `autocue:` protocol to automatically compute cue points and crossfade parameters (#3753, @RM-FM and @Moonbase59)
- Added `db_levels` method to `blank.*` sources (#3790)

Fixed:

Expand Down
64 changes: 34 additions & 30 deletions src/core/operators/noblank.ml
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,26 @@ class virtual base ~start_blank ~track_sensitive ~max_blank ~min_noise
but it has been silent for l samples;
- `Blank l: the source is considered to be silent,
but it has been noisy for l samples. *)
val mutable state = if start_blank then `Blank 0 else `Noise 0
val state = Atomic.make (if start_blank then `Blank 0 else `Noise 0)

val dB_levels = Atomic.make None
method dB_levels = Atomic.get dB_levels
method virtual private log : Log.t
method is_blank = match state with `Blank _ -> true | _ -> false

method is_blank =
match Atomic.get state with `Blank _ -> true | _ -> false

method private string_of_state =
function `Blank _ -> "blank" | `Noise _ -> "no blank"

method private set_state s =
begin
match (state, s) with
match (Atomic.get state, s) with
| `Blank _, `Noise _ | `Noise _, `Blank _ ->
self#log#info "Setting state to %s" (self#string_of_state s)
| _ -> ()
end;
state <- s
Atomic.set state s

(** This method should be called after the frame [s] has been
filled, where [p0] is the position in [s] before filling. *)
Expand All @@ -63,11 +67,12 @@ class virtual base ~start_blank ~track_sensitive ~max_blank ~min_noise
else (
let len = AFrame.position s - p0 in
let rms = AFrame.rms s p0 len in
Atomic.set dB_levels (Some rms);
let threshold = threshold () in
let noise =
Array.fold_left (fun noise r -> noise || r > threshold) false rms
in
match state with
match Atomic.get state with
| `Noise blank_len ->
if noise then (if blank_len <> 0 then self#set_state (`Noise 0))
else (
Expand Down Expand Up @@ -256,20 +261,33 @@ let extract p =
let ts = Lang.to_bool_getter (f "track_sensitive") in
(start_blank, max_blank, min_noise, threshold, ts, s)

let meth () =
[
( "dB_levels",
([], Lang.fun_t [] (Lang.nullable_t (Lang.list_t Lang.float_t))),
"Return the detected dB level for each channel.",
fun s ->
Lang.val_fun [] (fun _ ->
match s#dB_levels with
| None -> Lang.null
| Some lvl ->
Lang.list
Array.(
to_list
(map (fun v -> Lang.float (Audio.dB_of_lin v)) lvl))) );
( "is_blank",
([], Lang.fun_t [] Lang.bool_t),
"Indicate whether blank was detected.",
fun s -> Lang.val_fun [] (fun _ -> Lang.bool s#is_blank) );
]

let _ =
let frame_t =
Lang.frame_t (Lang.univ_t ())
(Frame.Fields.make ~audio:(Format_type.audio ()) ())
in
Lang.add_operator ~base:Blank.blank "detect" ~return_t:frame_t
~category:`Track
~meth:
[
( "is_blank",
([], Lang.fun_t [] Lang.bool_t),
"Indicate whether blank was detected.",
fun s -> Lang.val_fun [] (fun _ -> Lang.bool s#is_blank) );
]
~category:`Track ~meth:(meth ())
~descr:"Calls a given handler when detecting a blank."
(( "",
Lang.fun_t [] Lang.unit_t,
Expand All @@ -296,18 +314,10 @@ let _ =
Lang.frame_t (Lang.univ_t ())
(Frame.Fields.make ~audio:(Format_type.audio ()) ())
in
Lang.add_operator ~base:Blank.blank "strip" ~return_t:frame_t
~meth:
[
( "is_blank",
([], Lang.fun_t [] Lang.bool_t),
"Indicate whether blank was detected.",
fun s -> Lang.val_fun [] (fun _ -> Lang.bool s#is_blank) );
]
Lang.add_operator ~base:Blank.blank "strip" ~return_t:frame_t ~meth:(meth ())
~category:`Track
~descr:"Make the source unavailable when it is streaming blank."
(proto frame_t)
(fun p ->
(proto frame_t) (fun p ->
let start_blank, max_blank, min_noise, threshold, track_sensitive, s =
extract p
in
Expand All @@ -321,13 +331,7 @@ let _ =
(Frame.Fields.make ~audio:(Format_type.audio ()) ())
in
Lang.add_operator ~base:Blank.blank "eat" ~return_t:frame_t ~category:`Track
~meth:
[
( "is_blank",
([], Lang.fun_t [] Lang.bool_t),
"Indicate whether blank was detected.",
fun s -> Lang.val_fun [] (fun _ -> Lang.bool s#is_blank) );
]
~meth:(meth ())
~descr:
"Eat blanks, i.e., drop the contents of the stream until it is not blank \
anymore."
Expand Down
Loading