liquidsoap-prettier 1.8.2 → 1.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/.github/workflows/check-formatting.yml +32 -0
  2. package/README.md +31 -5
  3. package/package.json +1 -1
  4. package/src/cli.js +104 -9
  5. package/tests/liq/audio.liq +460 -0
  6. package/tests/liq/autocue.liq +1081 -0
  7. package/tests/liq/clock.liq +14 -0
  8. package/tests/liq/cron.liq +74 -0
  9. package/tests/liq/error.liq +48 -0
  10. package/tests/liq/extra/audio.liq +677 -0
  11. package/tests/liq/extra/audioscrobbler.liq +482 -0
  12. package/tests/liq/extra/deprecations.liq +976 -0
  13. package/tests/liq/extra/externals.liq +196 -0
  14. package/tests/liq/extra/fades.liq +260 -0
  15. package/tests/liq/extra/file.liq +66 -0
  16. package/tests/liq/extra/http.liq +160 -0
  17. package/tests/liq/extra/interactive.liq +917 -0
  18. package/tests/liq/extra/metadata.liq +75 -0
  19. package/tests/liq/extra/native.liq +201 -0
  20. package/tests/liq/extra/openai.liq +150 -0
  21. package/tests/liq/extra/server.liq +177 -0
  22. package/tests/liq/extra/source.liq +476 -0
  23. package/tests/liq/extra/spinitron.liq +272 -0
  24. package/tests/liq/extra/telnet.liq +266 -0
  25. package/tests/liq/extra/video.liq +59 -0
  26. package/tests/liq/extra/visualization.liq +68 -0
  27. package/tests/liq/fades.liq +941 -0
  28. package/tests/liq/ffmpeg.liq +605 -0
  29. package/tests/liq/file.liq +387 -0
  30. package/tests/liq/getter.liq +74 -0
  31. package/tests/liq/hls.liq +329 -0
  32. package/tests/liq/http.liq +1048 -0
  33. package/tests/liq/http_codes.liq +447 -0
  34. package/tests/liq/icecast.liq +58 -0
  35. package/tests/liq/io.liq +106 -0
  36. package/tests/liq/liquidsoap.liq +31 -0
  37. package/tests/liq/list.liq +440 -0
  38. package/tests/liq/log.liq +47 -0
  39. package/tests/liq/lufs.liq +295 -0
  40. package/tests/liq/math.liq +23 -0
  41. package/tests/liq/medialib.liq +752 -0
  42. package/tests/liq/metadata.liq +253 -0
  43. package/tests/liq/nfo.liq +258 -0
  44. package/tests/liq/null.liq +71 -0
  45. package/tests/liq/playlist.liq +1347 -0
  46. package/tests/liq/predicate.liq +106 -0
  47. package/tests/liq/process.liq +93 -0
  48. package/tests/liq/profiler.liq +5 -0
  49. package/tests/liq/protocols.liq +1139 -0
  50. package/tests/liq/ref.liq +28 -0
  51. package/tests/liq/replaygain.liq +135 -0
  52. package/tests/liq/request.liq +467 -0
  53. package/tests/liq/resolvers.liq +33 -0
  54. package/tests/liq/runtime.liq +70 -0
  55. package/tests/liq/server.liq +99 -0
  56. package/tests/liq/settings.liq +41 -0
  57. package/tests/liq/socket.liq +33 -0
  58. package/tests/liq/source.liq +362 -0
  59. package/tests/liq/sqlite.liq +161 -0
  60. package/tests/liq/stdlib.liq +172 -0
  61. package/tests/liq/string.liq +476 -0
  62. package/tests/liq/switches.liq +197 -0
  63. package/tests/liq/testing.liq +37 -0
  64. package/tests/liq/thread.liq +161 -0
  65. package/tests/liq/tracks.liq +100 -0
  66. package/tests/liq/utils.liq +81 -0
  67. package/tests/liq/video.liq +918 -0
@@ -0,0 +1,917 @@
1
+ # Information about all variables
2
+ # @category Interaction
3
+ variables = ref([])
4
+
5
+ # Float variables
6
+ # @category Interaction
7
+ variables_float = ref([])
8
+
9
+ # Int variables
10
+ # @category Interaction
11
+ variables_int = ref([])
12
+
13
+ # Bool variables
14
+ # @category Interaction
15
+ variables_bool = ref([])
16
+
17
+ # String variables
18
+ # @category Interaction
19
+ variables_string = ref([])
20
+
21
+ # Unit variables: those are not references but handler functions
22
+ # @category Interaction
23
+ variables_unit = ref([])
24
+ let interactive = ()
25
+ let interactive.float = ()
26
+ let interactive.int = ()
27
+ let interactive.bool = ()
28
+ let interactive.string = ()
29
+ let interactive.unit = ()
30
+ let interactive.error = error.register("interactive.error")
31
+
32
+ # @flag hidden
33
+ def interactive.list(_) =
34
+ l = variables()
35
+ l =
36
+ list.map(
37
+ fun (xv) ->
38
+ begin
39
+ let (x, v) = xv
40
+ "#{x} : #{v.type}"
41
+ end,
42
+ l
43
+ )
44
+
45
+ string.concat(separator="\n", l)
46
+ end
47
+
48
+ server.register(
49
+ usage="list",
50
+ description="List available interactive variables.",
51
+ namespace="var",
52
+ "list",
53
+ interactive.list
54
+ )
55
+
56
+ # Description of an interactive variable.
57
+ # @flag hidden
58
+ def interactive.description(name) =
59
+ list.assoc(name, variables()).description
60
+ end
61
+
62
+ # Type of an interactive variable.
63
+ # @flag hidden
64
+ def interactive.type(name) =
65
+ list.assoc(name, variables()).type
66
+ end
67
+
68
+ # @flag hidden
69
+ def interactive.float.ref(name) =
70
+ list.assoc(name, variables_float()).ref
71
+ end
72
+
73
+ # @flag hidden
74
+ def interactive.int.ref(name) =
75
+ list.assoc(name, variables_int()).ref
76
+ end
77
+
78
+ # @flag hidden
79
+ def interactive.bool.ref(name) =
80
+ list.assoc(name, variables_bool()).ref
81
+ end
82
+
83
+ # @flag hidden
84
+ def interactive.string.ref(name) =
85
+ list.assoc(name, variables_string()).ref
86
+ end
87
+
88
+ # @flag hidden
89
+ def interactive.unit.handler(name) =
90
+ list.assoc(name, variables_unit()).handler
91
+ end
92
+
93
+ # @flag hidden
94
+ def interactive.remove(name) =
95
+ variables := list.assoc.remove.all(name, variables())
96
+ end
97
+
98
+ # Remove an interactive variable.
99
+ # @param name Name of the variable.
100
+ # @category Interaction
101
+ # @flag hidden
102
+ def interactive.float.remove(name) =
103
+ interactive.remove(name)
104
+ variables_float := list.assoc.remove.all(name, variables_float())
105
+ end
106
+
107
+ # Remove an interactive variable.
108
+ # @param name Name of the variable.
109
+ # @category Interaction
110
+ # @flag hidden
111
+ def interactive.int.remove(name) =
112
+ interactive.remove(name)
113
+ variables_int := list.assoc.remove.all(name, variables_int())
114
+ end
115
+
116
+ # Remove an interactive variable.
117
+ # @param name Name of the variable.
118
+ # @category Interaction
119
+ # @flag hidden
120
+ def interactive.bool.remove(name) =
121
+ interactive.remove(name)
122
+ variables_bool := list.assoc.remove.all(name, variables_bool())
123
+ end
124
+
125
+ # Remove an interactive variable.
126
+ # @param name Name of the variable.
127
+ # @category Interaction
128
+ # @flag hidden
129
+ def interactive.string.remove(name) =
130
+ interactive.remove(name)
131
+ variables_string := list.assoc.remove.all(name, variables_string())
132
+ end
133
+
134
+ # Remove an interactive variable.
135
+ # @param name Name of the variable.
136
+ # @category Interaction
137
+ # @flag hidden
138
+ def interactive.unit.remove(name) =
139
+ interactive.remove(name)
140
+ variables_unit := list.assoc.remove.all(name, variables_unit())
141
+ end
142
+
143
+ # Function called to ensure persistency of data.
144
+ # @category Interaction
145
+ let interactive.persistency = ref(fun () -> ())
146
+
147
+ # @flag hidden
148
+ def interactive.float.set(name, v) =
149
+ interactive.float.ref(name) := v
150
+ p = interactive.persistency()
151
+ p()
152
+ end
153
+
154
+ # @flag hidden
155
+ def interactive.int.set(name, v) =
156
+ interactive.int.ref(name) := v
157
+ p = interactive.persistency()
158
+ p()
159
+ end
160
+
161
+ # @flag hidden
162
+ def interactive.bool.set(name, v) =
163
+ interactive.bool.ref(name) := v
164
+ p = interactive.persistency()
165
+ p()
166
+ end
167
+
168
+ # @flag hidden
169
+ def interactive.string.set(name, v) =
170
+ interactive.string.ref(name) := v
171
+ p = interactive.persistency()
172
+ p()
173
+ end
174
+
175
+ # @flag hidden
176
+ def interactive.unit.set(name) =
177
+ f = interactive.unit.handler(name)
178
+ f()
179
+ end
180
+
181
+ %ifdef osc.on_float
182
+ let stdlib_osc = osc
183
+ %endif
184
+
185
+ # Create an interactive variable.
186
+ # @flag hidden
187
+ # @param ~name Name of the variable.
188
+ # @param ~description Description of the variable.
189
+ # @param ~osc OSC address for the variable.
190
+ # @param ~type Type of the variable.
191
+ def interactive.create(~name, ~description="", ~osc="", ~type) =
192
+ if
193
+ list.assoc.mem(name, variables())
194
+ then
195
+ error.raise(
196
+ interactive.error,
197
+ "variable already registered"
198
+ )
199
+ end
200
+
201
+ variables := (name, {type = type, description = description})::variables()
202
+ variables :=
203
+ list.sort(
204
+ fun (n, n') -> if fst(n) < fst(n') then -1 else 1 end,
205
+ variables()
206
+ )
207
+
208
+ %ifdef osc.on_float
209
+ if
210
+ osc != ""
211
+ then
212
+ if
213
+ type == "float"
214
+ then
215
+ stdlib_osc.on_float(osc, fun (x) -> interactive.float.set(name, x))
216
+ elsif
217
+ type == "int"
218
+ then
219
+ stdlib_osc.on_int(osc, fun (x) -> interactive.int.set(name, x))
220
+ elsif
221
+ type == "bool"
222
+ then
223
+ stdlib_osc.on_bool(osc, fun (x) -> interactive.bool.set(name, x))
224
+ elsif
225
+ type == "string"
226
+ then
227
+ stdlib_osc.on_string(osc, fun (x) -> interactive.string.set(name, x))
228
+ elsif
229
+ type == "unit"
230
+ then
231
+ ()
232
+ else
233
+ # TODO
234
+ error.raise(error.not_found)
235
+ end
236
+ end
237
+ %else
238
+ ignore(osc)
239
+ %endif
240
+ end
241
+
242
+ # @flag hidden
243
+ def interactive.get(name) =
244
+ try
245
+ t = interactive.type(name)
246
+ if
247
+ t == "float"
248
+ then
249
+ r = interactive.float.ref(name)
250
+ string.float(decimal_places=3, r())
251
+ elsif
252
+ t == "int"
253
+ then
254
+ r = interactive.int.ref(name)
255
+ string(r())
256
+ elsif
257
+ t == "bool"
258
+ then
259
+ r = interactive.bool.ref(name)
260
+ string(r())
261
+ elsif
262
+ t == "string"
263
+ then
264
+ r = interactive.string.ref(name)
265
+ r()
266
+ elsif
267
+ t == "unit"
268
+ then
269
+ "()"
270
+ else
271
+ error.raise(error.not_found)
272
+ end
273
+ catch _ do
274
+ "Variable not found."
275
+ end
276
+ end
277
+
278
+ server.register(
279
+ namespace="var",
280
+ description="Get the value of a variable.",
281
+ "get",
282
+ interactive.get
283
+ )
284
+
285
+ # @flag hidden
286
+ def interactive.set(arg) =
287
+ try
288
+ arg = r/=/.split(arg)
289
+ name = string.trim(list.nth(arg, 0))
290
+ value = string.trim(list.nth(arg, 1))
291
+ t = interactive.type(name)
292
+ if
293
+ t == "float"
294
+ then
295
+ interactive.float.set(name, float_of_string(value))
296
+ elsif
297
+ t == "int"
298
+ then
299
+ interactive.int.set(name, int_of_string(value))
300
+ elsif
301
+ t == "bool"
302
+ then
303
+ interactive.bool.set(name, bool_of_string(value))
304
+ elsif
305
+ t == "string"
306
+ then
307
+ interactive.string.set(name, value)
308
+ elsif
309
+ t == "unit"
310
+ then
311
+ interactive.unit.set(name)
312
+ else
313
+ error.raise(error.not_found)
314
+ end
315
+
316
+ "Variable #{name} set."
317
+ catch _ do
318
+ "Syntax error or variable not found."
319
+ end
320
+ end
321
+
322
+ server.register(
323
+ usage="set <name> = <value>",
324
+ description="Set the value of a variable.",
325
+ namespace="var",
326
+ "set",
327
+ interactive.set
328
+ )
329
+
330
+ # Save the value of all interactive variables in a file.
331
+ # @category Interaction
332
+ # @flag extra
333
+ # @param fname Name of the file.
334
+ def interactive.save(fname) =
335
+ data =
336
+ json.stringify(
337
+ {
338
+ float =
339
+ list.map(
340
+ fun (nv) ->
341
+ begin
342
+ let (name, v) = nv
343
+ (name, v.ref())
344
+ end,
345
+ variables_float()
346
+ ),
347
+ int =
348
+ list.map(
349
+ fun (nv) ->
350
+ begin
351
+ let (name, v) = nv
352
+ (name, v.ref())
353
+ end,
354
+ variables_int()
355
+ ),
356
+ bool =
357
+ list.map(
358
+ fun (nv) ->
359
+ begin
360
+ let (name, v) = nv
361
+ (name, v.ref())
362
+ end,
363
+ variables_bool()
364
+ ),
365
+ string =
366
+ list.map(
367
+ fun (nv) ->
368
+ begin
369
+ let (name, v) = nv
370
+ (name, v.ref())
371
+ end,
372
+ variables_string()
373
+ )
374
+ }
375
+ )
376
+
377
+ file.write(data=data, fname)
378
+ end
379
+
380
+ # Load the value of interactive variables from a file.
381
+ # @category Interaction
382
+ # @flag extra
383
+ # @param fname Name of the file.
384
+ def interactive.load(fname) =
385
+ vars = file.contents(fname)
386
+ let json.parse (vars :
387
+ {
388
+ float: [(string * float)],
389
+ int: [(string * int)],
390
+ bool: [(string * bool)],
391
+ string: [(string * string)]
392
+ }
393
+ ) = vars
394
+
395
+ list.iter(
396
+ fun (nv) ->
397
+ try
398
+ interactive.float.set(fst(nv), snd(nv))
399
+ catch _ do
400
+ log.important(
401
+ label="interactive.load",
402
+ "Variable #{fst(nv)} not found."
403
+ )
404
+ end,
405
+ vars.float
406
+ )
407
+
408
+ list.iter(
409
+ fun (nv) ->
410
+ try
411
+ interactive.int.set(fst(nv), snd(nv))
412
+ catch _ do
413
+ log.important(
414
+ label="interactive.load",
415
+ "Variable #{fst(nv)} not found."
416
+ )
417
+ end,
418
+ vars.int
419
+ )
420
+
421
+ list.iter(
422
+ fun (nv) ->
423
+ try
424
+ interactive.bool.set(fst(nv), snd(nv))
425
+ catch _ do
426
+ log.important(
427
+ label="interactive.load",
428
+ "Variable #{fst(nv)} not found."
429
+ )
430
+ end,
431
+ vars.bool
432
+ )
433
+
434
+ list.iter(
435
+ fun (nv) ->
436
+ try
437
+ interactive.string.set(fst(nv), snd(nv))
438
+ catch _ do
439
+ log.important(
440
+ label="interactive.load",
441
+ "Variable #{fst(nv)} not found."
442
+ )
443
+ end,
444
+ vars.string
445
+ )
446
+ end
447
+
448
+ # Make the value of interactive variables persistent: they are loaded from the
449
+ # given file and stored there whenever updated. This function should be called
450
+ # after all interactive variables have been defined (variables not declared yet
451
+ # will not be loaded).
452
+ # @category Interaction
453
+ # @flag extra
454
+ # @param fname Name of the file.
455
+ def interactive.persistent(fname) =
456
+ if
457
+ file.exists(fname)
458
+ then
459
+ interactive.load(fname)
460
+ else
461
+ interactive.save(fname)
462
+ end
463
+
464
+ interactive.persistency := {interactive.save(fname)}
465
+ end
466
+
467
+ # Expose interactive variables through harbor http server. Once this is called,
468
+ # with default parameters, you can browse <http://localhost:8000/interactive> to
469
+ # change the value of interactive variables using sliders.
470
+ # @category Interaction
471
+ # @flag extra
472
+ # @param ~port Port of the server.
473
+ # @param ~transport Http transport. Use `http.transport.ssl` or http.transport.secure_transport`, when available, to enable HTTPS output
474
+ # @param ~uri URI of the server.
475
+ def interactive.harbor(
476
+ ~transport=http.transport.unix,
477
+ ~port=8000,
478
+ ~uri="/interactive"
479
+ ) =
480
+ def webpage(request, response) =
481
+ form_data = request.data
482
+ data = ref("")
483
+
484
+ def add(s) =
485
+ data := data() ^ s ^ "\n"
486
+ end
487
+
488
+ title =
489
+ "Interactive values"
490
+ add(
491
+ "<!DOCTYPE html><html><head>"
492
+ )
493
+ add(
494
+ "<meta charset='utf-8'/>"
495
+ )
496
+ add("<title>#{title}</title>")
497
+ add(
498
+ "<style>
499
+ body {
500
+ font-family: sans-serif;
501
+ }
502
+ h1 {
503
+ text-align: center;
504
+ }
505
+ form {
506
+ border-radius: 20px;
507
+ display: block;
508
+ background-color: whitesmoke;
509
+ width: max-content;
510
+ margin: 0 auto;
511
+ padding: 2ex;
512
+ display:grid;
513
+ grid-template-columns: max-content max-content;
514
+ grid-gap: 5px;
515
+ }
516
+ label {
517
+ text-align: right;
518
+ }
519
+ input {
520
+ width: 300px;
521
+ }
522
+ </style>"
523
+ )
524
+
525
+ # TODO: we could send only the updated value instead of sending them all
526
+ add(
527
+ "<script>
528
+ function send() {
529
+ let interactive = document.getElementsByClassName('interactive');
530
+ let data = '';
531
+ for(var i=0; i<interactive.length; i++){
532
+ if (interactive[i].type == 'checkbox') {
533
+ if (interactive[i].checked) {
534
+ interactive[i].value = 'true'
535
+ } else {
536
+ interactive[i].value = 'false'
537
+ }
538
+ }
539
+ if (interactive[i].type != 'button') {
540
+ data = data.concat(interactive[i].name+'='+encodeURIComponent(interactive[i].value))+(i < interactive.length - 1 ? '&' : '')
541
+ }
542
+ }
543
+ console.log('Posting: ' + data);
544
+ let xmlHttp = new XMLHttpRequest();
545
+ xmlHttp.open('POST', '#{
546
+ uri
547
+ }');
548
+ xmlHttp.onreadystatechange = function () {
549
+ if(xmlHttp.readyState === XMLHttpRequest.DONE) {
550
+ var status = xmlHttp.status;
551
+ if (status === 0 || (status >= 200 && status < 400)) {
552
+ //console.log(xmlHttp.responseText);
553
+ } else {
554
+ console.log('Failed to send values.')
555
+ }
556
+ }
557
+ }
558
+ xmlHttp.send(data);
559
+ }
560
+ function sendUnit(name) {
561
+ const body = name + '=';
562
+ console.log('Posting: ' + body);
563
+ fetch('#{
564
+ uri
565
+ }', {method: 'POST', body: body});
566
+ }
567
+ </script>"
568
+ )
569
+
570
+ add("</head><body>")
571
+ add("<h1>#{title}</h1>")
572
+
573
+ def add_var((name, v)) =
574
+ description = interactive.description(name)
575
+ description =
576
+ if
577
+ description == ""
578
+ then
579
+ name
580
+ else
581
+ "#{description} (#{name})"
582
+ end
583
+
584
+ add(
585
+ "<label for=#{name}>#{string.escape.html(description)}</label>"
586
+ )
587
+ common =
588
+ "id='#{name}' name='#{name}' class='interactive' onchange=\"send()\""
589
+
590
+ if
591
+ v.type == "float"
592
+ then
593
+ v = list.assoc(name, variables_float())
594
+ value = http.string_of_float(v.ref())
595
+ if
596
+ v.min == 0. - infinity or v.max == infinity
597
+ then
598
+ add(
599
+ "<input type='number' #{common} step='#{v.step}' value='#{value}'>"
600
+ )
601
+ else
602
+ min = http.string_of_float(v.min)
603
+ max = http.string_of_float(v.max)
604
+ step = http.string_of_float(v.step)
605
+ value = http.string_of_float(v.ref())
606
+ unit =
607
+ if
608
+ v.unit == ""
609
+ then
610
+ ""
611
+ else
612
+ " " ^
613
+ v.unit
614
+ end
615
+ add(
616
+ "<div><input type='range' #{common} min='#{min}' max='#{max}' step='#{
617
+ step
618
+ }' value='#{value}' oninput='document.getElementById(\"#{
619
+ name
620
+ }-value\").innerHTML = this.value+\"#{unit}\"'><text id='#{
621
+ name
622
+ }-value' style='inline'>#{value}#{unit}</text></div>"
623
+ )
624
+ end
625
+ elsif
626
+ v.type == "int"
627
+ then
628
+ v = list.assoc(name, variables_int())
629
+ value = string(v.ref())
630
+ add(
631
+ "<input type='number' #{common} step='1' value='#{value}'>"
632
+ )
633
+ elsif
634
+ v.type == "bool"
635
+ then
636
+ v = list.assoc(name, variables_bool())
637
+ c = (v.ref()) ? "checked" : ""
638
+ add(
639
+ "<input type='checkbox' #{common} value='true' #{c}>"
640
+ )
641
+ elsif
642
+ v.type == "string"
643
+ then
644
+ v = list.assoc(name, variables_string())
645
+ add(
646
+ "<input type='text' #{common} value='#{string.escape.html(v.ref())}'>"
647
+ )
648
+ elsif
649
+ v.type == "unit"
650
+ then
651
+ add(
652
+ "<button type='button' #{common} onclick=\"sendUnit('#{name}')\">#{
653
+ name
654
+ }</button>"
655
+ )
656
+ else
657
+ ()
658
+ end
659
+ end
660
+
661
+ add("<form>")
662
+ list.iter(add_var, variables())
663
+ add("</form>")
664
+ add("</body>")
665
+ response.html(data())
666
+ end
667
+
668
+ harbor.http.register(
669
+ transport=transport,
670
+ port=port,
671
+ method="GET",
672
+ uri,
673
+ webpage
674
+ )
675
+
676
+ def setter(request, _) =
677
+ data = url.split_args(request.body())
678
+
679
+ def update((name, v)) =
680
+ try
681
+ t = interactive.type(name)
682
+ if
683
+ t == "float"
684
+ then
685
+ interactive.float.set(name, float_of_string(v))
686
+ elsif
687
+ t == "int"
688
+ then
689
+ interactive.int.set(name, int_of_string(v))
690
+ elsif
691
+ t == "bool"
692
+ then
693
+ interactive.bool.set(name, bool_of_string(v))
694
+ elsif
695
+ t == "string"
696
+ then
697
+ interactive.string.set(name, v)
698
+ elsif
699
+ t == "unit"
700
+ then
701
+ interactive.unit.set(name)
702
+ end
703
+ catch e do
704
+ log.important(
705
+ label="interactive.harbor",
706
+ "Could not update variable #{name} (#{e.kind}: #{e.message})."
707
+ )
708
+ end
709
+ end
710
+
711
+ list.iter(update, data)
712
+ end
713
+
714
+ harbor.http.register(
715
+ transport=transport,
716
+ port=port,
717
+ method="POST",
718
+ uri,
719
+ setter
720
+ )
721
+
722
+ log.important(
723
+ label="interactive.harbor",
724
+ "Website should be ready at <http://localhost:#{port}#{uri}>."
725
+ )
726
+ end
727
+
728
+ # Read a float from an interactive input.
729
+ # @category Interaction
730
+ # @flag extra
731
+ # @param ~min Minimal value.
732
+ # @param ~max Maximal value.
733
+ # @param ~step Typical variation of the value.
734
+ # @param ~description Description of the variable.
735
+ # @param ~unit Unit for the variable.
736
+ # @param ~osc OSC address.
737
+ # @param name Name of the variable.
738
+ # @param v Initial value.
739
+ def replaces interactive.float(
740
+ ~min=0. - infinity,
741
+ ~max=infinity,
742
+ ~step=0.1,
743
+ ~description="",
744
+ ~unit="",
745
+ ~osc="",
746
+ name,
747
+ v
748
+ ) =
749
+ interactive.create(name=name, description=description, osc=osc, type="float")
750
+ r = ref(v)
751
+ variables_float :=
752
+ (
753
+ name,
754
+ {ref = r, unit = unit, min = min, max = max, step = step}
755
+ )::variables_float()
756
+
757
+ r.{
758
+ set = fun (x) -> interactive.float.set(name, x),
759
+ remove = {interactive.float.remove(name)}
760
+ }
761
+ end
762
+
763
+ # Read an integer from an interactive input.
764
+ # @category Interaction
765
+ # @flag extra
766
+ # @param ~description Description of the variable.
767
+ # @param ~osc OSC address.
768
+ # @param name Name of the variable.
769
+ # @param v Initial value.
770
+ def replaces interactive.int(~description="", ~osc="", name, v) =
771
+ interactive.create(name=name, description=description, osc=osc, type="int")
772
+ r = ref(v)
773
+ variables_int := (name, {ref = r})::variables_int()
774
+ r.{
775
+ set = fun (x) -> interactive.int.set(name, x),
776
+ remove = {interactive.int.remove(name)}
777
+ }
778
+ end
779
+
780
+ # Read a boolean from an interactive input.
781
+ # @category Interaction
782
+ # @flag extra
783
+ # @param ~description Description of the variable.
784
+ # @param ~osc OSC address.
785
+ # @param name Name of the variable.
786
+ # @param v Initial value.
787
+ def replaces interactive.bool(~description="", ~osc="", name, v) =
788
+ interactive.create(name=name, description=description, osc=osc, type="bool")
789
+ r = ref(v)
790
+ variables_bool := (name, {ref = r})::variables_bool()
791
+ r.{
792
+ set = fun (x) -> interactive.bool.set(name, x),
793
+ remove = {interactive.bool.remove(name)}
794
+ }
795
+ end
796
+
797
+ # Read a string from an interactive input.
798
+ # @category Interaction
799
+ # @flag extra
800
+ # @param ~description Description of the variable.
801
+ # @param ~osc OSC address.
802
+ # @param name Name of the variable.
803
+ # @param v Initial value.
804
+ def replaces interactive.string(~description="", ~osc="", name, v) =
805
+ interactive.create(name=name, description=description, osc=osc, type="string")
806
+ r = ref(v)
807
+ variables_string := (name, {ref = r})::variables_string()
808
+ r.{
809
+ set = fun (x) -> interactive.string.set(name, x),
810
+ remove = {interactive.string.remove(name)}
811
+ }
812
+ end
813
+
814
+ # Register a callback when a unit interactive input is set.
815
+ # @category Interaction
816
+ # @flag extra
817
+ # @param ~description Description of the variable.
818
+ # @param ~osc OSC address.
819
+ # @param name Name of the variable.
820
+ # @param f Function triggered when the value is set.
821
+ def replaces interactive.unit(~description="", ~osc="", name, f) =
822
+ interactive.create(name=name, description=description, osc=osc, type="unit")
823
+ variables_unit := (name, {handler = f})::variables_unit()
824
+ {set = f, remove = {interactive.float.remove(name)}}
825
+ end
826
+
827
+ # Create a multiband compressor whose parameters are interactive variables.
828
+ # @category Interaction
829
+ # @flag extra
830
+ # @param ~id Id of the source. Variable names are prefixed with this.
831
+ # @param ~bands Number of bands.
832
+ # @param s Source to compress.
833
+ def compress.multiband.interactive(~id=null, ~bands=5, s) =
834
+ id = string.id.default(default="compress.multiband.interactive", id)
835
+ prefix = id ^ "_"
836
+ wet = interactive.float(prefix ^ "wet", min=0., max=1., 1.)
837
+ min_freq = 100.
838
+ max_freq = 15000.
839
+
840
+ def band(i) =
841
+ frequency =
842
+ exp(
843
+ (ln(max_freq) - ln(min_freq)) * float_of_int(i) /
844
+ float_of_int(bands - 1) +
845
+ ln(min_freq)
846
+ )
847
+
848
+ log.important(
849
+ label=id,
850
+ "Adding a band at #{frequency} Hz."
851
+ )
852
+ frequency =
853
+ interactive.float(
854
+ "#{prefix}frequency#{i}",
855
+ unit="Hz",
856
+ min=0.,
857
+ max=20000.,
858
+ step=10.,
859
+ frequency
860
+ )
861
+
862
+ attack =
863
+ interactive.float(
864
+ "#{prefix}attack#{i}",
865
+ unit="ms",
866
+ min=0.,
867
+ max=1000.,
868
+ step=10.,
869
+ 100.
870
+ )
871
+
872
+ release =
873
+ interactive.float(
874
+ "#{prefix}release#{i}",
875
+ unit="ms",
876
+ min=0.,
877
+ max=1000.,
878
+ step=10.,
879
+ 200.
880
+ )
881
+
882
+ threshold =
883
+ interactive.float(
884
+ "#{prefix}threshold#{i}",
885
+ unit="dB",
886
+ min=-20.,
887
+ max=0.,
888
+ step=0.1,
889
+ -10.
890
+ )
891
+
892
+ ratio =
893
+ interactive.float("#{prefix}ratio#{i}", min=1., max=10., step=0.1, 4.)
894
+
895
+ gain =
896
+ interactive.float(
897
+ "#{prefix}gain#{i}",
898
+ unit="dB",
899
+ min=0.,
900
+ max=30.,
901
+ step=0.1,
902
+ 3.
903
+ )
904
+
905
+ {
906
+ frequency = frequency,
907
+ attack = attack,
908
+ release = release,
909
+ threshold = threshold,
910
+ ratio = ratio,
911
+ gain = gain
912
+ }
913
+ end
914
+
915
+ l = list.init(bands, band)
916
+ compress.multiband(wet=wet, s, l)
917
+ end