Skip to content

Commit 3b956b2

Browse files
committed
Add new 'watch' effect.
1 parent 7cbddfe commit 3b956b2

10 files changed

+451
-20
lines changed

GNUmakefile

+9-7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ DSP_OBJ := dsp.o \
2222
noise.o \
2323
dither.o \
2424
stats.o \
25+
watch.o \
2526
null.o \
2627
sgen.o \
2728
pcm.o
@@ -42,20 +43,21 @@ LADSPA_DSP_OBJ := ladspa_dsp.o \
4243
decorrelate.o \
4344
noise.o \
4445
dither.o \
45-
stats.o
46+
stats.o \
47+
watch.o
4648
LADSPA_DSP_CPP_OBJ :=
4749

48-
BASE_CFLAGS := -Os -Wall -std=gnu99
49-
BASE_CXXFLAGS := -Os -Wall -std=gnu++11
50-
BASE_LDFLAGS :=
50+
BASE_CFLAGS := -Os -Wall -std=gnu99 -pthread
51+
BASE_CXXFLAGS := -Os -Wall -std=gnu++11 -pthread
52+
BASE_LDFLAGS := -pthread
5153
BASE_LIBS := -lm
5254

5355
include config.mk
5456

5557
DEPFLAGS := -MMD -MP
56-
DSP_CFLAGS := ${DEPFLAGS} ${BASE_CFLAGS} -pthread ${DSP_EXTRA_CFLAGS} ${CFLAGS} ${CPPFLAGS}
57-
DSP_CXXFLAGS := ${DEPFLAGS} ${BASE_CXXFLAGS} -pthread ${DSP_EXTRA_CFLAGS} ${CXXFLAGS} ${CPPFLAGS}
58-
DSP_LDFLAGS := ${BASE_LDFLAGS} -pthread ${LDFLAGS}
58+
DSP_CFLAGS := ${DEPFLAGS} ${BASE_CFLAGS} ${DSP_EXTRA_CFLAGS} ${CFLAGS} ${CPPFLAGS}
59+
DSP_CXXFLAGS := ${DEPFLAGS} ${BASE_CXXFLAGS} ${DSP_EXTRA_CFLAGS} ${CXXFLAGS} ${CPPFLAGS}
60+
DSP_LDFLAGS := ${BASE_LDFLAGS} ${LDFLAGS}
5961
DSP_LIBS := ${DSP_EXTRA_LIBS} ${BASE_LIBS}
6062
LADSPA_DSP_CFLAGS := ${DEPFLAGS} ${BASE_CFLAGS} -fPIC -DPIC -DLADSPA_FRONTEND -DSYMMETRIC_IO ${LADSPA_DSP_EXTRA_CFLAGS} ${CFLAGS} ${CPPFLAGS}
6163
LADSPA_DSP_CXXFLAGS := ${DEPFLAGS} ${BASE_CXXFLAGS} -fPIC -DPIC -DLADSPA_FRONTEND -DSYMMETRIC_IO ${LADSPA_DSP_EXTRA_CFLAGS} ${CXXFLAGS} ${CPPFLAGS}

README.md

+21
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,27 @@ Example:
347347
(dBFS), crest factor (dB), peak count, peak sample, number of samples, and
348348
length (s) for each channel. If `ref_level` is given, peak and RMS levels
349349
relative to `ref_level` will be shown as well (dBr).
350+
* `watch [-e] [~/]path`
351+
Load effects from a file into a sub-chain and reload if the file is
352+
modified. Other than the automatic reload, the behavior is similar to
353+
sourcing a file using the `@` directive (see "Effects Files"). Some
354+
restrictions apply to automatic reload:
355+
356+
* The new sub-chain must have the same output sample rate and number of
357+
channels as the previous sub-chain.
358+
* The new sub-chain must not require larger buffers than the previous
359+
sub-chain.
360+
361+
If these conditions are not met, the new sub-chain will not be applied and
362+
an error message will be printed.
363+
364+
Currently, this effect polls for file modifications once per second.
365+
Support `inotify` events my be added in the future. Ideally, file
366+
modifications should be atomic (i.e. by writing to a temporary file, then
367+
`rename(3)`-ing it over top of the original file). If this is not possible,
368+
the `-e` flag may be given, which enforces an end-of-file marker in order
369+
to detect partially-written files. This marker, `#EOF#`, must be placed at
370+
the beginning of a line and may only be followed by whitespace characters.
350371

351372
#### Selector syntax
352373

dsp.1

+26
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,32 @@ Display the DC offset, minimum, maximum, peak level (dBFS), RMS level
373373
(dBFS), crest factor (dB), peak count, peak sample, number of samples, and
374374
length (s) for each channel. If \fIref_level\fR is given, peak and RMS levels
375375
relative to \fIref_level\fR will be shown as well (dBr).
376+
.TP
377+
\fBwatch\fR [\fI\-e\fR] [~/]\fIpath\fR
378+
Load effects from a file into a sub-chain and reload if the file is
379+
modified. Other than the automatic reload, the behavior is similar to
380+
sourcing a file using the `@' directive (see ``Effects Files''). Some
381+
restrictions apply to automatic reload:
382+
.RS
383+
.IP \(bu 3
384+
The new sub-chain must have the same output sample rate and number of
385+
channels as the previous sub-chain.
386+
.IP \(bu 3
387+
The new sub-chain must not require larger buffers than the previous
388+
sub-chain.
389+
.RE
390+
.TP
391+
\
392+
If these conditions are not met, the new sub-chain will not be applied and
393+
an error message will be printed.
394+
.sp 0.5
395+
Currently, this effect polls for file modifications once per second.
396+
Support `inotify` events my be added in the future. Ideally, file
397+
modifications should be atomic (i.e. by writing to a temporary file, then
398+
\fIrename\fR(3)-ing it over top of the original file). If this is not possible,
399+
the \fI\-e\fR flag may be given, which enforces an end-of-file marker in order
400+
to detect partially-written files. This marker, `#EOF#', must be placed at
401+
the beginning of a line and may only be followed by whitespace characters.
376402
.SS Selector syntax
377403
.TS
378404
tab (|);

effect.c

+25-6
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "dither.h"
4343
#include "ladspa_host.h"
4444
#include "stats.h"
45+
#include "watch.h"
4546

4647
#define DO_EFFECTS_CHAIN_OPTIMIZE 1
4748

@@ -96,6 +97,7 @@ static const struct effect_info effects[] = {
9697
{ "ladspa_host", "ladspa_host module_path plugin_label [control ...]", ladspa_host_effect_init, 0 },
9798
#endif
9899
{ "stats", "stats [ref_level]", stats_effect_init, 0 },
100+
{ "watch", "watch [-e] [~/]path", watch_effect_init, 0 },
99101
};
100102

101103
const struct effect_info * get_effect_info(const char *name)
@@ -127,7 +129,7 @@ void append_effect(struct effects_chain *chain, struct effect *e)
127129
}
128130

129131
static int build_effects_chain_block(int, const char *const *, struct effects_chain *, struct stream_info *, const char *, const char *);
130-
static int build_effects_chain_from_file(struct effects_chain *, struct stream_info *, const char *, const char *, const char *);
132+
static int build_effects_chain_block_from_file(const char *, struct effects_chain *, struct stream_info *, const char *, const char *, int);
131133

132134
static int build_effects_chain_block(int argc, const char *const *argv, struct effects_chain *chain, struct stream_info *stream, const char *initial_channel_mask, const char *dir)
133135
{
@@ -201,7 +203,7 @@ static int build_effects_chain_block(int argc, const char *const *argv, struct e
201203
last_stream_channels = stream->channels;
202204
}
203205
if (argv[k][0] == '@') {
204-
if (build_effects_chain_from_file(chain, stream, channel_selector, dir, &argv[k][1]))
206+
if (build_effects_chain_block_from_file(&argv[k][1], chain, stream, channel_selector, dir, 0))
205207
goto fail;
206208
++k;
207209
continue;
@@ -282,7 +284,7 @@ static int build_effects_chain_block(int argc, const char *const *argv, struct e
282284
return 1;
283285
}
284286

285-
static int build_effects_chain_from_file(struct effects_chain *chain, struct stream_info *stream, const char *channel_mask, const char *dir, const char *path)
287+
static int build_effects_chain_block_from_file(const char *path, struct effects_chain *chain, struct stream_info *stream, const char *channel_mask, const char *dir, int enforce_eof_marker)
286288
{
287289
char **argv = NULL, *tmp, *d = NULL, *p, *c;
288290
int i, ret = 0, argc = 0;
@@ -292,6 +294,15 @@ static int build_effects_chain_from_file(struct effects_chain *chain, struct str
292294
LOG_FMT(LL_ERROR, "error: failed to load effects file: %s: %s", p, strerror(errno));
293295
goto fail;
294296
}
297+
if (enforce_eof_marker) {
298+
const ssize_t l = LENGTH(EFFECTS_FILE_EOF_MARKER)-1;
299+
ssize_t k = strlen(c);
300+
while (k > l && IS_WHITESPACE(c[k-1])) --k;
301+
if (k < l || strncmp(&c[k-l], EFFECTS_FILE_EOF_MARKER, l) != 0 || (k > l && c[k-l-1] != '\n')) {
302+
LOG_FMT(LL_ERROR, "error: no valid end-of-file marker: %s", p);
303+
goto fail;
304+
}
305+
}
295306
if (gen_argv_from_string(c, &argc, &argv))
296307
goto fail;
297308
d = strdup(p);
@@ -364,11 +375,19 @@ static void effects_chain_optimize(struct effects_chain *chain)
364375
#endif
365376
}
366377

378+
367379
int build_effects_chain(int argc, const char *const *argv, struct effects_chain *chain, struct stream_info *stream, const char *dir)
368380
{
369-
int r;
370-
if ((r = build_effects_chain_block(argc, argv, chain, stream, NULL, dir)))
371-
return r;
381+
int r = build_effects_chain_block(argc, argv, chain, stream, NULL, dir);
382+
if (r) return r;
383+
effects_chain_optimize(chain);
384+
return 0;
385+
}
386+
387+
int build_effects_chain_from_file(const char *path, struct effects_chain *chain, struct stream_info *stream, const char *channel_mask, const char *dir, int enforce_eof_marker)
388+
{
389+
int r = build_effects_chain_block_from_file(path, chain, stream, channel_mask, dir, enforce_eof_marker);
390+
if (r) return r;
372391
effects_chain_optimize(chain);
373392
return 0;
374393
}

effect.h

+2
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,13 @@ struct effects_chain {
6868
|| strcmp(x, "!") == 0 \
6969
|| strcmp(x, "{") == 0 \
7070
)
71+
#define EFFECTS_FILE_EOF_MARKER "#EOF#"
7172

7273
const struct effect_info * get_effect_info(const char *);
7374
void destroy_effect(struct effect *);
7475
void append_effect(struct effects_chain *, struct effect *);
7576
int build_effects_chain(int, const char *const *, struct effects_chain *, struct stream_info *, const char *);
77+
int build_effects_chain_from_file(const char *, struct effects_chain *, struct stream_info *, const char *, const char *, int);
7678
ssize_t get_effects_chain_buffer_len(struct effects_chain *, ssize_t, int);
7779
ssize_t get_effects_chain_max_out_frames(struct effects_chain *, ssize_t);
7880
int effects_chain_needs_dither(struct effects_chain *);

ladspa_dsp.c

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* This file is part of dsp.
33
*
4-
* Copyright (c) 2014-2024 Michael Barbour <[email protected]>
4+
* Copyright (c) 2014-2025 Michael Barbour <[email protected]>
55
*
66
* Permission to use, copy, modify, and distribute this software for any
77
* purpose with or without fee is hereby granted, provided that the above
@@ -21,6 +21,7 @@
2121
#include <stdio.h>
2222
#include <string.h>
2323
#include <signal.h>
24+
#include <pthread.h>
2425
#include <sys/types.h>
2526
#include <dirent.h>
2627
#include <errno.h>
@@ -57,9 +58,17 @@ struct dsp_globals dsp_globals = {
5758
static int n_configs = 0;
5859
static struct ladspa_dsp_config *configs = NULL;
5960
static LADSPA_Descriptor *descriptors = NULL;
61+
static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
6062

61-
void dsp_log_acquire(void) { /* do nothing */ }
62-
void dsp_log_release(void) { /* do nothing */ }
63+
void dsp_log_acquire(void)
64+
{
65+
pthread_mutex_lock(&log_lock);
66+
}
67+
68+
void dsp_log_release(void)
69+
{
70+
pthread_mutex_unlock(&log_lock);
71+
}
6372

6473
static void init_config(struct ladspa_dsp_config *config, const char *file_name, const char *dir_path)
6574
{

util.c

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* This file is part of dsp.
33
*
4-
* Copyright (c) 2013-2024 Michael Barbour <[email protected]>
4+
* Copyright (c) 2013-2025 Michael Barbour <[email protected]>
55
*
66
* Permission to use, copy, modify, and distribute this software for any
77
* purpose with or without fee is hereby granted, provided that the above
@@ -224,8 +224,6 @@ int num_bits_set(const char *b, int n)
224224
return c;
225225
}
226226

227-
#define IS_WHITESPACE(x) (x == ' ' || x == '\t' || x == '\n')
228-
229227
static void strip_char(char *str, char c, int is_esc)
230228
{
231229
int i = 0, k = 0, esc = 0;

util.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* This file is part of dsp.
33
*
4-
* Copyright (c) 2013-2024 Michael Barbour <[email protected]>
4+
* Copyright (c) 2013-2025 Michael Barbour <[email protected]>
55
*
66
* Permission to use, copy, modify, and distribute this software for any
77
* purpose with or without fee is hereby granted, provided that the above
@@ -52,6 +52,7 @@
5252
#endif
5353
#define TEST_BIT(x, o, s) (!!GET_BIT(x, o) == !!(s))
5454
#define IS_POWER_OF_2(x) ((x) && !((x)&((x)-1)))
55+
#define IS_WHITESPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\n' || (x) == '\r')
5556
#define PM_RAND_MAX 0x7fffffff
5657

5758
int check_endptr(const char *, const char *, const char *, const char *);

0 commit comments

Comments
 (0)