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 support for the proprietary stereotool operator. #2953

Merged
merged 1 commit into from
Mar 14, 2023
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 dune-project
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
bjack
camlimages
camomile
ctypes-foreign
dssi
faad
fdkaac
Expand Down
1 change: 1 addition & 0 deletions liquidsoap.opam
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ depopts: [
"bjack"
"camlimages"
"camomile"
"ctypes-foreign"
"dssi"
"faad"
"fdkaac"
Expand Down
2 changes: 2 additions & 0 deletions src/config/stereotool_option.disabled.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
let detected = "no (requires ctypes-foreign)"
let enabled = false
1 change: 1 addition & 0 deletions src/config/stereotool_option.enabled.ml
14 changes: 14 additions & 0 deletions src/core/dune
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,14 @@
(optional)
(modules ssl_base builtins_http_ssl))

(library
(name liquidsoap_stereotool)
(libraries stereotool liquidsoap_core)
(library_flags -linkall)
(wrapped false)
(optional)
(modules stereotool_op))

(library
(name liquidsoap_taglib)
(libraries taglib liquidsoap_core)
Expand Down Expand Up @@ -751,6 +759,7 @@
speex_option
srt_option
ssl_option
stereotool_option
taglib_option
theora_option
vorbis_option
Expand Down Expand Up @@ -974,6 +983,11 @@
from
(liquidsoap_ssl -> ssl_option.enabled.ml)
(-> ssl_option.disabled.ml))
(select
stereotool_option.ml
from
(liquidsoap_stereotool -> stereotool_option.enabled.ml)
(-> stereotool_option.disabled.ml))
(select
taglib_option.ml
from
Expand Down
169 changes: 169 additions & 0 deletions src/core/operators/stereotool_op.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
(*****************************************************************************

Liquidsoap, a programmable audio stream generator.
Copyright 2003-2023 Savonet team

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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 General Public License for more details, fully stated in the COPYING
file at the root of the liquidsoap distribution.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

*****************************************************************************)

let load_type_of_string = function
| "totalinit" -> `Totalinit
| "all_settings" -> `All_settings
| "audiofm" -> `Audiofm
| "audio" -> `Audio
| "processing" -> `Processing
| "repair" -> `Repair
| "repair_no_pnr" -> `Repair_no_pnr
| "sublevel_pnr" -> `Sublevel_pnr
| _ -> raise Not_found

class stereotool ~field ~preset ~load_type ~handler source =
object (self)
inherit Source.operator ~name:"stereotool" [source] as super
method api_version = Stereotool.api_version handler
method software_version = Stereotool.software_version handler

method private load_type load_type =
try load_type_of_string load_type
with Not_found ->
self#log#important "Invalid load type: %s" load_type;
`Totalinit

method load_preset ~load_type filename =
Stereotool.load_preset ~load_type:(self#load_type load_type) ~filename
handler

method valid_license = Stereotool.valid_license handler

method unlincensed_used_features =
Stereotool.unlincensed_used_features handler

val mutable latency = None

method latency =
match latency with
| Some v -> v
| None ->
let v =
Frame.seconds_of_audio
(Stereotool.latency
~samplerate:(Lazy.force Frame.audio_rate)
~feed_silence:true handler)
in
latency <- Some v;
v

method! wake_up l =
super#wake_up l;
(match preset with
| None -> ()
| Some filename ->
if not (self#load_preset ~load_type filename) then
self#log#important "Preset load failed!");
self#log#info
"Stereotool initialized! Valid license: %b, latency: %.02fs, \
API/software version: %d/%d"
self#valid_license self#latency self#api_version self#software_version;
match Stereotool.unlincensed_used_features handler with
| None -> ()
| Some s -> self#log#info "Using unlicensed features: %s" s

method stype = source#stype
method remaining = source#remaining
method seek = source#seek
method is_ready = source#is_ready
method abort_track = source#abort_track
method self_sync = source#self_sync

method private get_frame buf =
let offset = AFrame.position buf in
source#get buf;
let position = AFrame.position buf in
let b = Content.Audio.get_data (Frame.get buf field) in
Stereotool.process
~samplerate:(Lazy.force Frame.audio_rate)
handler b offset (position - offset)
end

let _ =
let frame_t = Format_type.audio () in
Lang.add_track_operator ~base:Modules.track_audio "stereotool"
[
( "library_file",
Lang.string_t,
None,
Some "Path to the shared library file." );
("license_key", Lang.nullable_t Lang.string_t, Some Lang.null, None);
( "preset",
Lang.nullable_t Lang.string_t,
Some Lang.null,
Some "Path to a preset file to load when initializing the operator." );
( "load_type",
Lang.string_t,
Some (Lang.string "totalinit"),
Some
"Load type for preset. One of: \"totalinit\", \"all_settings\", \
\"audiofm\", \"audio\", \"processing\", \"repair\", \
\"repair_no_pnr\" or \"sublevel_pnr\"." );
("", frame_t, None, None);
]
~meth:
[
( "api_version",
([], Lang.fun_t [] Lang.int_t),
"API version.",
fun s -> Lang.val_fun [] (fun _ -> Lang.int s#api_version) );
( "software_version",
([], Lang.fun_t [] Lang.int_t),
"Software version.",
fun s -> Lang.val_fun [] (fun _ -> Lang.int s#api_version) );
( "latency",
([], Lang.fun_t [] Lang.float_t),
"Get the operator's latency.",
fun s -> Lang.val_fun [] (fun _ -> Lang.float s#latency) );
( "valid_license",
([], Lang.fun_t [] Lang.bool_t),
"Check if the license is valid for the current settings.",
fun s -> Lang.val_fun [] (fun _ -> Lang.bool s#valid_license) );
( "unlincensed_used_features",
([], Lang.fun_t [] (Lang.nullable_t Lang.string_t)),
"Check if the license is valid for the current settings.",
fun s ->
Lang.val_fun [] (fun _ ->
match s#unlincensed_used_features with
| None -> Lang.null
| Some s -> Lang.string s) );
]
~return_t:frame_t ~category:`Audio
~descr:"Process the given audio track with StereoTool."
(fun p ->
let library = Lang.to_string (List.assoc "library_file" p) in
let license_key =
Lang.to_valued_option Lang.to_string (List.assoc "license_key" p)
in
let load_type = Lang.to_string (List.assoc "load_type" p) in
let preset =
Lang.to_valued_option Lang.to_string (List.assoc "preset" p)
in
let handler =
try Stereotool.init ?license_key ~filename:library ()
with Stereotool.Library_not_found ->
Runtime_error.raise ~pos:(Lang.pos p)
~message:"Stereotool library not found or invalid!" "invalid"
in
let field, src = Lang.to_track (List.assoc "" p) in
(field, new stereotool ~field ~preset ~load_type ~handler src))
5 changes: 1 addition & 4 deletions src/core/types/format_type.ml
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,7 @@ let media ~strict () =
| Type.Constr { params } when not strict ->
List.iter (fun (_, typ) -> satisfies typ) params
| Type.Meth _ when not strict ->
let meths, base_type = Type.split_meths b in
List.iter
(fun Type.{ scheme = _, field_type } -> satisfies field_type)
meths;
let _, base_type = Type.split_meths b in
satisfies base_type
| Type.Custom { Type.typ = Kind _ }
| Type.Custom { Type.typ = Format _ } ->
Expand Down
3 changes: 2 additions & 1 deletion src/lang/parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,8 @@ record_pattern:
| LCUR meth_pattern_list RCUR { $2 }

meth_spread_list:
| DOTDOTDOT VAR { Some (PVar [$2]), [] }
| DOTDOTDOT { Some (PVar ["_"]), [] }
| DOTDOTDOT optvar { Some (PVar [$2]), [] }
| meth_pattern_el COMMA meth_spread_list { fst $3, $1::(snd $3) }

record_spread_pattern:
Expand Down
16 changes: 16 additions & 0 deletions src/libs/audio.liq
Original file line number Diff line number Diff line change
Expand Up @@ -544,3 +544,19 @@ def metronome(~frequency=440., bpm=60.)

amplify(f,s)
end

%ifdef track.audio.stereotool
# Process an audio source using stereotool
# @argsof track.audio.stereotool
# @category Source / Audio processing
def stereotool(~id=null("stereotool"), %argsof(track.audio.stereotool[!id]), s) =
let {audio, metadata, track_marks, ...} = source.tracks(s)
s = track.audio.stereotool(%argsof(track.audio.stereotool[!id]), audio)
let replaces s = source(id=id, {
audio = (s:pcm),
metadata = metadata,
track_marks = track_marks
})
s
end
%endif
1 change: 1 addition & 0 deletions src/runtime/build_config.ml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ let build_config =
- Lilv : %{Lilv_option.detected}
- Samplerate : %{Samplerate_option.detected}
- SoundTouch : %{Soundtouch_option.detected}
- StereoTool : %{Stereotool_option.detected}

* Video manipulation
- camlimages : %{Camlimages_option.detected}
Expand Down
5 changes: 5 additions & 0 deletions src/stereotool/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(library
(optional)
(name stereotool)
(modules stereotool)
(libraries ctypes ctypes.foreign))
Loading