Skip to content

Commit

Permalink
Add support for the proprietary stereotool operator.
Browse files Browse the repository at this point in the history
  • Loading branch information
toots committed Mar 14, 2023
1 parent f24714c commit 25c05e6
Show file tree
Hide file tree
Showing 14 changed files with 396 additions and 6 deletions.
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

0 comments on commit 25c05e6

Please sign in to comment.