~ubuntu-branches/ubuntu/trusty/liquidsoap/trusty

« back to all changes in this revision

Viewing changes to .pc/no_local_ladspa.patch/src/operators/ladspa_op.ml

  • Committer: Bazaar Package Importer
  • Author(s): Romain Beauxis
  • Date: 2011-10-18 16:43:01 UTC
  • Revision ID: james.westby@ubuntu.com-20111018164301-491ju2x39ymu0ct9
Tags: 1.0.0-3
Better patch to compute lo dymanic plugin dependencies.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
(*****************************************************************************
2
 
 
3
 
  Liquidsoap, a programmable audio stream generator.
4
 
  Copyright 2003-2011 Savonet team
5
 
 
6
 
  This program is free software; you can redistribute it and/or modify
7
 
  it under the terms of the GNU General Public License as published by
8
 
  the Free Software Foundation; either version 2 of the License, or
9
 
  (at your option) any later version.
10
 
 
11
 
  This program is distributed in the hope that it will be useful,
12
 
  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 
  GNU General Public License for more details, fully stated in the COPYING
15
 
  file at the root of the liquidsoap distribution.
16
 
 
17
 
  You should have received a copy of the GNU General Public License
18
 
  along with this program; if not, write to the Free Software
19
 
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
 
 
21
 
 *****************************************************************************)
22
 
 
23
 
open Source
24
 
open Ladspa
25
 
open Dtools
26
 
 
27
 
type t = Float | Int | Bool
28
 
 
29
 
let log = Log.make ["LADSPA extension"]
30
 
 
31
 
let ladspa_enable =
32
 
  try
33
 
    let venv = Unix.getenv "LIQ_LADSPA" in
34
 
      venv = "1" || venv = "true"
35
 
  with
36
 
    | Not_found -> true
37
 
 
38
 
let plugin_dirs =
39
 
  try
40
 
    let path = Unix.getenv "LIQ_LADSPA_PATH" in
41
 
      Pcre.split ~pat:":" path
42
 
  with
43
 
    | Not_found -> ["/usr/lib/ladspa";"/usr/local/lib/ladspa"]
44
 
 
45
 
 
46
 
let port_t d p =
47
 
  if Descriptor.port_is_boolean d p then Bool
48
 
  else if Descriptor.port_is_integer d p then Int
49
 
  else Float
50
 
 
51
 
class virtual base ~kind source =
52
 
object
53
 
  inherit operator kind [source] as super
54
 
 
55
 
  method stype = source#stype
56
 
 
57
 
  method remaining = source#remaining
58
 
 
59
 
  method is_ready = source#is_ready
60
 
 
61
 
  method abort_track = source#abort_track
62
 
end
63
 
 
64
 
class virtual base_nosource ~kind =
65
 
object
66
 
  inherit source kind
67
 
 
68
 
  method stype = Infallible
69
 
 
70
 
  method is_ready = true
71
 
 
72
 
  val mutable must_fail = false
73
 
 
74
 
  method abort_track =
75
 
    must_fail <- true
76
 
 
77
 
  method remaining = -1
78
 
end
79
 
 
80
 
(* A plugin is created for each channel. *)
81
 
class ladspa ~kind (source:source) plugin descr input output params =
82
 
object (self)
83
 
  inherit base ~kind source
84
 
 
85
 
  val inst =
86
 
    let p = Plugin.load plugin in
87
 
    let d = Descriptor.descriptor p descr in
88
 
      Array.init ((Frame.type_of_kind kind).Frame.audio)
89
 
        (fun _ ->
90
 
           Descriptor.instantiate
91
 
             d
92
 
             (Lazy.force Frame.audio_rate)
93
 
             (AFrame.size ()))
94
 
 
95
 
  initializer
96
 
    Array.iter Descriptor.activate inst
97
 
 
98
 
  method private get_frame buf =
99
 
    let offset = AFrame.position buf in
100
 
    source#get buf;
101
 
    let b = AFrame.content buf offset in
102
 
    let position = AFrame.position buf in
103
 
    let len = position - offset in
104
 
      for c = 0 to Array.length b - 1 do
105
 
        Descriptor.set_samples inst.(c) len;
106
 
        Descriptor.connect_audio_port inst.(c) input b.(c) offset;
107
 
        Descriptor.connect_audio_port inst.(c) output b.(c) offset;
108
 
        List.iter
109
 
          (fun (p,v) -> Descriptor.connect_control_port_in inst.(c) p (v ()))
110
 
          params;
111
 
        Descriptor.run inst.(c)
112
 
      done
113
 
end
114
 
 
115
 
class ladspa_nosource ~kind plugin descr output params =
116
 
object (self)
117
 
  inherit base_nosource ~kind
118
 
 
119
 
  val inst =
120
 
    let p = Plugin.load plugin in
121
 
    let d = Descriptor.descriptor p descr in
122
 
      Array.init ((Frame.type_of_kind kind).Frame.audio)
123
 
        (fun _ ->
124
 
           Descriptor.instantiate
125
 
             d
126
 
             (Lazy.force Frame.audio_rate)
127
 
             (AFrame.size ()))
128
 
 
129
 
  initializer
130
 
    Array.iter Descriptor.activate inst
131
 
 
132
 
  method private get_frame buf =
133
 
    if must_fail then
134
 
      (
135
 
        AFrame.add_break buf (AFrame.position buf);
136
 
        must_fail <- false
137
 
      )
138
 
    else
139
 
      let offset = AFrame.position buf in
140
 
      let b = AFrame.content buf offset in
141
 
      let position = AFrame.size () in
142
 
      let len = position - offset in
143
 
        for c = 0 to Array.length b - 1 do
144
 
          Descriptor.set_samples inst.(c) len;
145
 
          Descriptor.connect_audio_port inst.(c) output b.(c) offset;
146
 
          List.iter
147
 
            (fun (p,v) -> Descriptor.connect_control_port_in inst.(c) p (v ()))
148
 
            params;
149
 
          Descriptor.run inst.(c)
150
 
        done;
151
 
        AFrame.add_break buf position
152
 
end
153
 
 
154
 
(* The plugin handles stereo streams. *)
155
 
class ladspa_stereo ~kind (source:source) plugin descr inputs outputs params =
156
 
object (self)
157
 
  inherit base ~kind source
158
 
 
159
 
  val inst =
160
 
    let p = Plugin.load plugin in
161
 
    let d = Descriptor.descriptor p descr in
162
 
      Descriptor.instantiate
163
 
        d
164
 
        (Lazy.force Frame.audio_rate)
165
 
        (AFrame.size ())
166
 
 
167
 
  initializer
168
 
    Descriptor.activate inst
169
 
 
170
 
  method private get_frame buf =
171
 
    let offset = AFrame.position buf in
172
 
    source#get buf;
173
 
    let b = AFrame.content buf offset in
174
 
    let position = AFrame.position buf in
175
 
    let len = position - offset in
176
 
      List.iter
177
 
        (fun (p,v) -> Descriptor.connect_control_port_in inst p (v ()))
178
 
        params;
179
 
      Descriptor.set_samples inst len;
180
 
      for c = 0 to 1 do
181
 
        Descriptor.connect_audio_port inst inputs.(c) b.(c) offset;
182
 
        Descriptor.connect_audio_port inst outputs.(c) b.(c) offset;
183
 
      done;
184
 
      Descriptor.run inst
185
 
end
186
 
 
187
 
class ladspa_stereo_nosource ~kind plugin descr outputs params =
188
 
object (self)
189
 
  inherit base_nosource ~kind
190
 
 
191
 
  val inst =
192
 
    let p = Plugin.load plugin in
193
 
    let d = Descriptor.descriptor p descr in
194
 
      Descriptor.instantiate
195
 
        d
196
 
        (Lazy.force Frame.audio_rate)
197
 
        (AFrame.size ())
198
 
 
199
 
  initializer
200
 
    Descriptor.activate inst
201
 
 
202
 
  method private get_frame buf =
203
 
    if must_fail then
204
 
      (
205
 
        AFrame.add_break buf (AFrame.position buf);
206
 
        must_fail <- false
207
 
      )
208
 
    else
209
 
      let offset = AFrame.position buf in
210
 
      let b = AFrame.content buf offset in
211
 
      let position = AFrame.size () in
212
 
      let len = position - offset in
213
 
        List.iter
214
 
          (fun (p,v) -> Descriptor.connect_control_port_in inst p (v ()))
215
 
          params;
216
 
        Descriptor.set_samples inst len;
217
 
        for c = 0 to 1 do
218
 
          Descriptor.connect_audio_port inst outputs.(c) b.(c) offset;
219
 
        done;
220
 
        Descriptor.run inst;
221
 
        AFrame.add_break buf position
222
 
end
223
 
 
224
 
let norm_string s =
225
 
  let s =
226
 
    Pcre.substitute
227
 
      ~pat:"( *\\([^\\)]*\\)| *\\[[^\\]]*\\])"
228
 
      ~subst:(fun _ -> "") s
229
 
  in
230
 
  let s = Pcre.substitute ~pat:"(\\.+|\\++)" ~subst:(fun _ -> "") s in
231
 
  let s = Pcre.substitute ~pat:" +$" ~subst:(fun _ -> "") s in
232
 
  let s = Pcre.substitute ~pat:"( +|/+|-+)" ~subst:(fun _ -> "_") s in
233
 
  let s = String.lowercase s in
234
 
  (* Identifiers cannot begin with a digit. *)
235
 
  let s = if Pcre.pmatch ~pat:"^[0-9]" s then "_"^s else s in
236
 
    s
237
 
 
238
 
(* List the indexes of control ports. *)
239
 
let get_control_ports d =
240
 
  let ports = Descriptor.port_count d in
241
 
  let ans = ref [] in
242
 
    for i = 0 to ports - 1 do
243
 
      if Descriptor.port_is_control d i && Descriptor.port_is_input d i then
244
 
        ans := i :: !ans;
245
 
    done;
246
 
    List.rev !ans
247
 
 
248
 
(** When creating operator for LADSPA plugins, we don't know yet at
249
 
  * which samplerate liquidsoap will operate. But the default values and
250
 
  * bounds for LADSPA parameters might depend on the samplerate.
251
 
  * Lacking a better solution, we use the following default samplerate,
252
 
  * potentially creating a mismatch between the doc and the actual
253
 
  * behavior. *)
254
 
let default_samplerate = 44100
255
 
 
256
 
(* Make a parameter for each control port.
257
 
 * Returns the liquidsoap parameters and the parameters for the plugin. *)
258
 
let params_of_descr d =
259
 
  let control_ports = get_control_ports d in
260
 
  let liq_params =
261
 
    let univ = ref 0 in
262
 
      List.map
263
 
        (fun p ->
264
 
           let t = port_t d p in
265
 
             incr univ;
266
 
             norm_string (Descriptor.port_name d p),
267
 
             (match t with
268
 
                | Float -> Lang.float_getter_t !univ
269
 
                | Int -> Lang.int_t
270
 
                | Bool -> Lang.bool_t
271
 
             ),
272
 
             (match
273
 
                Descriptor.port_get_default d
274
 
                  ~samplerate:default_samplerate p
275
 
              with
276
 
                | Some f ->
277
 
                    Some
278
 
                      (match t with
279
 
                         | Float -> Lang.float f
280
 
                         | Int -> Lang.int (int_of_float f)
281
 
                         | Bool -> Lang.bool (f > 0.))
282
 
                | None -> None
283
 
             ),
284
 
             let bounds =
285
 
               let min =
286
 
                 Descriptor.port_get_min d
287
 
                   ~samplerate:default_samplerate p
288
 
               in
289
 
               let max =
290
 
                 Descriptor.port_get_max d
291
 
                   ~samplerate:default_samplerate p
292
 
               in
293
 
                 if (min, max) = (None, None) then ""
294
 
                 else
295
 
                   let bounds = ref " (" in
296
 
                     begin match min with
297
 
                       | Some f ->
298
 
                           begin match t with
299
 
                             | Float ->
300
 
                                 bounds :=
301
 
                                 Printf.sprintf "%s%.6g <= " !bounds f
302
 
                             | Int ->
303
 
                                 bounds :=
304
 
                                 Printf.sprintf "%s%d <= " !bounds
305
 
                                   (int_of_float (ceil f))
306
 
                             | Bool -> ()
307
 
                           end
308
 
                       | None -> ()
309
 
                     end ;
310
 
                     bounds :=
311
 
                     !bounds ^ (norm_string (Descriptor.port_name d p));
312
 
                     begin match max with
313
 
                       | Some f ->
314
 
                           begin match t with
315
 
                             | Float ->
316
 
                                 bounds :=
317
 
                                 Printf.sprintf "%s <= %.6g" !bounds f
318
 
                             | Int ->
319
 
                                 bounds :=
320
 
                                 Printf.sprintf "%s <= %d" !bounds
321
 
                                   (int_of_float f)
322
 
                             | Bool -> ()
323
 
                           end
324
 
                       | None -> ()
325
 
                     end ;
326
 
                     !bounds ^ ")"
327
 
             in
328
 
               Some (Descriptor.port_name d p ^ bounds ^ ".")
329
 
        )
330
 
        control_ports
331
 
  in
332
 
  let params p =
333
 
    let f v = List.assoc v p in
334
 
      List.map
335
 
        (fun p ->
336
 
           p,
337
 
           let v = f (norm_string (Descriptor.port_name d p)) in
338
 
             match port_t d p with
339
 
               | Float -> Lang.to_float_getter v
340
 
               | Int ->
341
 
                   let f = float_of_int (Lang.to_int v) in
342
 
                     fun () -> f
343
 
                       | Bool ->
344
 
                           let f = if Lang.to_bool v then 1. else 0. in
345
 
                             fun () -> f
346
 
        )
347
 
        control_ports
348
 
  in
349
 
    liq_params, params
350
 
 
351
 
 
352
 
let register_descr ?(stereo=false) plugin_name descr_n d inputs outputs =
353
 
  let k =
354
 
    Lang.kind_type_of_kind_format ~fresh:1
355
 
      (if stereo then Lang.audio_stereo else Lang.any_fixed)
356
 
  in
357
 
  let liq_params, params = params_of_descr d in
358
 
  let liq_params =
359
 
    liq_params@(if inputs = None then [] else ["", Lang.source_t k, None, None])
360
 
  in
361
 
  let descr = Printf.sprintf "%s by %s." (Descriptor.name d) (Descriptor.maker d) in
362
 
    Lang.add_operator ("ladspa." ^ norm_string (Descriptor.label d)) liq_params
363
 
      ~kind:(Lang.Unconstrained k)
364
 
      ~category:Lang.SoundProcessing
365
 
      ~flags:[Lang.Hidden]
366
 
      ~descr
367
 
      (fun p kind ->
368
 
         let f v = List.assoc v p in
369
 
         let source =
370
 
           try
371
 
             Some (Lang.to_source (f ""))
372
 
           with
373
 
             | Not_found -> None
374
 
         in
375
 
         let params = params p in
376
 
           match inputs with
377
 
             | Some inputs ->
378
 
                 if stereo then
379
 
                   new ladspa_stereo ~kind
380
 
                       (Utils.get_some source)
381
 
                       plugin_name
382
 
                       descr_n
383
 
                       inputs
384
 
                       outputs
385
 
                       params
386
 
                 else
387
 
                   new ladspa ~kind
388
 
                       (Utils.get_some source)
389
 
                       plugin_name
390
 
                       descr_n
391
 
                       inputs.(0)
392
 
                       outputs.(0)
393
 
                       params
394
 
             | None ->
395
 
                 if stereo then
396
 
                   new ladspa_stereo_nosource ~kind
397
 
                       plugin_name
398
 
                       descr_n
399
 
                       outputs
400
 
                       params
401
 
                 else
402
 
                   new ladspa_nosource ~kind
403
 
                       plugin_name
404
 
                       descr_n
405
 
                       outputs.(0)
406
 
                       params
407
 
      )
408
 
 
409
 
(** Get input and output ports. *)
410
 
let get_audio_ports d =
411
 
  let i = ref [] in
412
 
  let o = ref [] in
413
 
  let ports = Descriptor.port_count d in
414
 
    for n = 0 to ports - 1 do
415
 
      if Descriptor.port_is_audio d n then
416
 
        if Descriptor.port_is_input d n then
417
 
          i := n :: !i
418
 
        else
419
 
          o := n :: !o
420
 
    done;
421
 
    Array.of_list (List.rev !i), Array.of_list (List.rev !o)
422
 
 
423
 
(** Get the input and the output port. Raises [Not_found] if there is not
424
 
  * exactly one output and zero or one input. *)
425
 
let get_io d =
426
 
  let i, o = get_audio_ports d in
427
 
    (
428
 
      if Array.length i = 0 then
429
 
        None
430
 
      else if Array.length i = 1 then
431
 
        Some i.(0)
432
 
      else
433
 
        raise Not_found
434
 
    ),
435
 
    (
436
 
      if Array.length o = 1 then
437
 
        o.(0)
438
 
      else
439
 
        raise Not_found
440
 
    )
441
 
 
442
 
(* Same thing but for stereo I/O. *)
443
 
let get_stereo_io d =
444
 
  let i, o = get_audio_ports d in
445
 
    (
446
 
      if Array.length i = 0 then
447
 
        None
448
 
      else if Array.length i = 2 then
449
 
        Some i
450
 
      else
451
 
        raise Not_found
452
 
    ),
453
 
    (
454
 
      if Array.length o = 2 then
455
 
        o
456
 
      else
457
 
        raise Not_found
458
 
    )
459
 
 
460
 
let register_plugin pname =
461
 
  try
462
 
    let p = Plugin.load pname in
463
 
    let descr = Descriptor.descriptors p in
464
 
      Array.iteri
465
 
        (fun n d ->
466
 
           try
467
 
             let i, o = get_io d in
468
 
               register_descr pname n d
469
 
                 (match i with
470
 
                    | Some i -> Some [|i|]
471
 
                    | None -> None)
472
 
                 [|o|]
473
 
           with
474
 
             | Not_found ->
475
 
                 (
476
 
                   try
477
 
                     (* TODO: handle other number of channels *)
478
 
                     let i, o = get_stereo_io d in
479
 
                       register_descr ~stereo:true pname n d i o
480
 
                   with
481
 
                     | Not_found -> ()
482
 
                 )
483
 
        ) descr
484
 
      (* TODO: Unloading plugins makes liq segv. Don't do it for now. *)
485
 
      (* Plugin.unload p *)
486
 
  with
487
 
    | Plugin.Not_a_plugin -> ()
488
 
 
489
 
let register_plugins () =
490
 
  let add plugins_dir =
491
 
    try
492
 
      let dir = Unix.opendir plugins_dir in
493
 
        try
494
 
          while true do
495
 
            let f = Unix.readdir dir in
496
 
              if f <> "." && f <> ".." then
497
 
                register_plugin (plugins_dir ^ "/" ^ f)
498
 
          done
499
 
        with
500
 
          | End_of_file -> Unix.closedir dir
501
 
    with
502
 
      | Unix.Unix_error (e,_,_) ->
503
 
          log#f 4 "Error while loading directory %s: %s"
504
 
            plugins_dir (Unix.error_message e)
505
 
  in
506
 
    List.iter add plugin_dirs
507
 
 
508
 
let () =
509
 
  if ladspa_enable then
510
 
    register_plugins ()