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.
- package/.github/workflows/check-formatting.yml +32 -0
- package/README.md +31 -5
- package/package.json +1 -1
- package/src/cli.js +104 -9
- package/tests/liq/audio.liq +460 -0
- package/tests/liq/autocue.liq +1081 -0
- package/tests/liq/clock.liq +14 -0
- package/tests/liq/cron.liq +74 -0
- package/tests/liq/error.liq +48 -0
- package/tests/liq/extra/audio.liq +677 -0
- package/tests/liq/extra/audioscrobbler.liq +482 -0
- package/tests/liq/extra/deprecations.liq +976 -0
- package/tests/liq/extra/externals.liq +196 -0
- package/tests/liq/extra/fades.liq +260 -0
- package/tests/liq/extra/file.liq +66 -0
- package/tests/liq/extra/http.liq +160 -0
- package/tests/liq/extra/interactive.liq +917 -0
- package/tests/liq/extra/metadata.liq +75 -0
- package/tests/liq/extra/native.liq +201 -0
- package/tests/liq/extra/openai.liq +150 -0
- package/tests/liq/extra/server.liq +177 -0
- package/tests/liq/extra/source.liq +476 -0
- package/tests/liq/extra/spinitron.liq +272 -0
- package/tests/liq/extra/telnet.liq +266 -0
- package/tests/liq/extra/video.liq +59 -0
- package/tests/liq/extra/visualization.liq +68 -0
- package/tests/liq/fades.liq +941 -0
- package/tests/liq/ffmpeg.liq +605 -0
- package/tests/liq/file.liq +387 -0
- package/tests/liq/getter.liq +74 -0
- package/tests/liq/hls.liq +329 -0
- package/tests/liq/http.liq +1048 -0
- package/tests/liq/http_codes.liq +447 -0
- package/tests/liq/icecast.liq +58 -0
- package/tests/liq/io.liq +106 -0
- package/tests/liq/liquidsoap.liq +31 -0
- package/tests/liq/list.liq +440 -0
- package/tests/liq/log.liq +47 -0
- package/tests/liq/lufs.liq +295 -0
- package/tests/liq/math.liq +23 -0
- package/tests/liq/medialib.liq +752 -0
- package/tests/liq/metadata.liq +253 -0
- package/tests/liq/nfo.liq +258 -0
- package/tests/liq/null.liq +71 -0
- package/tests/liq/playlist.liq +1347 -0
- package/tests/liq/predicate.liq +106 -0
- package/tests/liq/process.liq +93 -0
- package/tests/liq/profiler.liq +5 -0
- package/tests/liq/protocols.liq +1139 -0
- package/tests/liq/ref.liq +28 -0
- package/tests/liq/replaygain.liq +135 -0
- package/tests/liq/request.liq +467 -0
- package/tests/liq/resolvers.liq +33 -0
- package/tests/liq/runtime.liq +70 -0
- package/tests/liq/server.liq +99 -0
- package/tests/liq/settings.liq +41 -0
- package/tests/liq/socket.liq +33 -0
- package/tests/liq/source.liq +362 -0
- package/tests/liq/sqlite.liq +161 -0
- package/tests/liq/stdlib.liq +172 -0
- package/tests/liq/string.liq +476 -0
- package/tests/liq/switches.liq +197 -0
- package/tests/liq/testing.liq +37 -0
- package/tests/liq/thread.liq +161 -0
- package/tests/liq/tracks.liq +100 -0
- package/tests/liq/utils.liq +81 -0
- 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
|