ifeq ("$(origin CC)", "default")
CC := cc
endif
+.ONESHELL:
+.SHELLFLAGS := -ec
LOGLEVELS := LL_DEBUG,LL_INFO,LL_NOTICE,LL_WARNING,LL_ERROR,LL_CRIT,LL_EMERG
vardir := /var/paraslash
$(object_dir) $(man_dir) $(dep_dir) $(m4depdir) $(lls_suite_dir) \
$(yy_build_dir):
- $(Q) $(MKDIR_P) $@
+ @$(MKDIR_P) $@
CPPFLAGS += -DBINDIR='"$(bindir)"'
CPPFLAGS += -DCOPYRIGHT_YEAR='"$(COPYRIGHT_YEAR)"'
# To put more focus on warnings, be less verbose as default
# Use 'make V=1' to see the full commands
ifeq ("$(origin V)", "command line")
- Q :=
+ SAY =
else
- Q := @
+ SAY = @echo '$(strip $(1))'
endif
audiod_commands := $(addprefix $(lls_suite_dir)/, \
$(man_dir)/para_%.1: $(lls_suite_dir)/%.lsg.man \
$(lls_m4_dir)/copyright.m4 | $(man_dir)
- @[ -z "$(Q)" ] || echo 'LLSMAN $<'
- $(Q) cat $< $(all_commands) > $@
- $(Q) $(M4) -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) $(lls_m4_dir)/copyright.m4 >> $@
+ $(call SAY, LLSMAN $<)
+ cat $< $(all_commands) > $@
+ $(M4) -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) $(lls_m4_dir)/copyright.m4 >> $@
$(object_dir)/%.o: %.c | $(object_dir)
$(object_dir)/compress_filter.o: CFLAGS += -O3
$(object_dir)/%.o: %.c | $(object_dir) $(dep_dir) $(lsg_h) $(yy_h)
- @[ -z "$(Q)" ] || echo 'CC $<'
- $(Q) $(CC) -c -o $@ -MMD -MF $(dep_dir)/$(*F).d -MT $@ $(CPPFLAGS) \
+ $(call SAY, CC $<)
+ $(CC) -c -o $@ -MMD -MF $(dep_dir)/$(*F).d -MT $@ $(CPPFLAGS) \
$(STRICT_CFLAGS) $(CFLAGS) $<
para_recv para_afh para_play para_server: LDFLAGS += $(id3tag_ldflags)
$(foreach exe,$(executables),$(eval para_$(exe): $$($(exe)_objs)))
$(prefixed_executables):
- @[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $^ -o $@ $(LDFLAGS)
+ $(call SAY, LD $@)
+ $(CC) $^ -o $@ $(LDFLAGS)
mostlyclean:
- @[ -z "$(Q)" ] || echo 'MOSTLYCLEAN'
- $(Q) rm -f para_*
- $(Q) rm -rf $(object_dir)
+ $(call SAY, MOSTLYCLEAN)
+ rm -f para_*
+ rm -rf $(object_dir)
clean: mostlyclean
- @[ -z "$(Q)" ] || echo 'CLEAN'
- $(Q) rm -rf $(build_dir)
+ $(call SAY, CLEAN)
+ rm -rf $(build_dir)
distclean: clean
- @[ -z "$(Q)" ] || echo 'DISTCLEAN'
- $(Q) rm -f Makefile autoscan.log config.status config.log
- $(Q) rm -f config.h configure config.h.in
+ $(call SAY, DISTCLEAN)
+ rm -f Makefile autoscan.log config.status config.log
+ rm -f config.h configure config.h.in
maintainer-clean: distclean
- @[ -z "$(Q)" ] || echo 'MAINTAINER-CLEAN'
- $(Q) rm -f *.tar.bz2 *.tar.xz
- $(Q) rm -f GPATH GRTAGS GSYMS GTAGS
+ $(call SAY, MAINTAINER-CLEAN)
+ rm -f *.tar.bz2 *.tar.xz
+ rm -f GPATH GRTAGS GSYMS GTAGS
INSTALL ?= install
INSTALL_PROGRAM ?= $(INSTALL)
$(MKDIR_P) $(DESTDIR)$(vardir) >/dev/null 2>&1 || true # not fatal, so don't complain
$(tarball) dist tarball:
- $(Q) rm -rf $(tarball) $(tarball_pfx)
- $(Q) git archive --format=tar --prefix=$(tarball_pfx)/ HEAD \
- | tar --delete $(tarball_delete) > $(tarball_pfx).tar
- $(Q) $(MKDIR_P) $(tarball_pfx)
- $(Q) ./GIT-VERSION-GEN > $(tarball_pfx)/VERSION
- $(Q) cp $(autocrap) $(tarball_pfx)
- $(Q) tar rf $(tarball_pfx).tar $(tarball_pfx)/*
- $(Q) xz -9 $(tarball_pfx).tar
- $(Q) ls -l $(tarball)
- $(Q) ln -sf $(tarball) paraslash-git.tar.xz
- $(Q) rm -rf $(tarball_pfx)
+ $(call SAY, DIST)
+ rm -rf $(tarball) $(tarball_pfx)
+ git archive --format=tar --prefix=$(tarball_pfx)/ HEAD \
+ | tar --delete $(tarball_delete) > $(tarball_pfx).tar
+ $(MKDIR_P) $(tarball_pfx)
+ ./GIT-VERSION-GEN > $(tarball_pfx)/VERSION
+ cp $(autocrap) $(tarball_pfx)
+ tar rf $(tarball_pfx).tar $(tarball_pfx)/*
+ xz -9 $(tarball_pfx).tar
+ ls -l $(tarball)
+ ln -sf $(tarball) paraslash-git.tar.xz
+ rm -rf $(tarball_pfx)
NEWS
====
+----------------------------------------------
+0.6.3 (to be announced) "generalized activity"
+----------------------------------------------
+
+- The ff command now accepts a negative argument to instruct the
+ virtual streaming system to jump backwards in the current audio
+ stream. The old syntax (e.g., "ff 30-") is still supported but it
+ is deprecated and no longer documented. The compatibility code is
+ sheduled for removal after 0.7.0.
+- para_afh: New option: --preserve to reset the modification time to
+ the value of the original file after meta data modification.
+- Overhaul of the compress filter code. The refined algorithm should
+ reduce clipping. The meaning of --aggressiveness has changed, see the
+ updated and extended documentation of the compress filter for details.
+- Cleanup of the audio format handler code.
+- We now build the tree using the .ONESHELL feature of GNU make,
+ which results in a significant speedup.
+- Two robustness fixes for FreeBSD.
+
+
--------------------------------------
0.6.2 (2018-06-30) "elastic diversity"
--------------------------------------
}
static const char * const aac_suffixes[] = {"m4a", "mp4", NULL};
+
/**
- * the init function of the aac audio format handler
+ * The audio format handler for the Advanced Audio Codec.
*
- * \param afh pointer to the struct to initialize
+ * This is only compiled in if the faad library is installed.
*/
-void aac_afh_init(struct audio_format_handler *afh)
-{
- afh->get_file_info = aac_get_file_info,
- afh->suffixes = aac_suffixes;
- afh->rewrite_tags = aac_afh_rewrite_tags;
- afh->open = aac_afh_open;
- afh->get_chunk = aac_afh_get_chunk;
- afh->close = aac_afh_close;
-}
+const struct audio_format_handler aac_afh = {
+ .get_file_info = aac_get_file_info,
+ .suffixes = aac_suffixes,
+ .rewrite_tags = aac_afh_rewrite_tags,
+ .open = aac_afh_open,
+ .get_chunk = aac_afh_get_chunk,
+ .close = aac_afh_close,
+};
goto out;
}
ret = xrename(tmp_name, name);
+ if (ret < 0)
+ goto out;
+ if (OPT_GIVEN(PRESERVE)) {
+ struct timespec times[2]; /* [0]: atime, [1]: mtime */
+ times[0].tv_nsec = UTIME_OMIT;
+ times[1] = sb.st_mtim;
+ /*
+ * We might well have written a file of identical size. If we
+ * keep the mtime as well, we might fool backup applications
+ * like rsync which skip files whose size and mtime haven't
+ * changed. So we change the mtime slightly.
+ */
+ times[1].tv_sec++;
+ if (futimens(output_fd, times) < 0) {
+ ret = -ERRNO_TO_PARA_ERROR(errno);
+ goto out;
+ }
+ }
out:
if (ret < 0 && output_fd >= 0)
unlink(tmp_name); /* ignore errors */
loglevel = OPT_UINT32_VAL(LOGLEVEL);
version_handle_flag("afh", OPT_GIVEN(VERSION));
handle_help_flags();
- afh_init();
for (i = 0; i < lls_num_inputs(lpr); i++) {
int ret2;
const char *path = lls_input(i, lpr);
* in the other part of this struct.
*/
struct audio_format_handler {
- /** Name of the audio format. */
- const char *name;
- /**
- * Pointer to the audio format handler's init function.
- *
- * Must initialize all function pointers and is assumed to succeed.
- */
- void (*init)(struct audio_format_handler*);
/** Typical file endings for files that can be handled by this afh. */
const char * const *suffixes;
/**
int output_fd, const char *filename);
};
-void afh_init(void);
int guess_audio_format(const char *name);
int compute_afhi(const char *path, char *data, size_t size,
int fd, struct afh_info *afhi);
#include "string.h"
#include "afh.h"
-typedef void afh_init_func(struct audio_format_handler *);
-
-/*
- * Declaration of the audio format handler init functions.
- *
- * These symbols are referenced in the afl array below.
- *
- * Most audio format handlers depend on an external library and are not
- * compiled in if the library is not installed. Hence it is well possible that
- * not all of these functions are defined. It does not hurt to declare them
- * anyway, and this avoids another set of ifdefs.
- */
-extern afh_init_func mp3_afh_init, ogg_afh_init, aac_afh_init, wma_afh_init,
- spx_afh_init, flac_afh_init, opus_afh_init;
-
/** The list of all status items */
const char *status_item_list[] = {STATUS_ITEMS};
/**
- * The list of supported audio formats.
+ * For each audio file the number of its audio format is stored in the
+ * database. Therefore this list, in particular its order, is part of the ABI.
+ * So it's only OK to append new audio formats. All audio formats are listed
+ * here, regardless of whether the audio format handler is compiled in.
+ */
+#define ALL_AUDIO_FORMATS \
+ AUDIO_FORMAT(mp3) \
+ AUDIO_FORMAT(ogg) \
+ AUDIO_FORMAT(aac) \
+ AUDIO_FORMAT(wma) \
+ AUDIO_FORMAT(spx) \
+ AUDIO_FORMAT(flac) \
+ AUDIO_FORMAT(opus) \
+
+/** \cond audio_format_handler */
+#define AUDIO_FORMAT(_fmt) #_fmt,
+static const char * const audio_format_names[] = {ALL_AUDIO_FORMATS};
+#undef AUDIO_FORMAT
+/* Weak declarations must be public. */
+#define AUDIO_FORMAT(_fmt) \
+ struct audio_format_handler _fmt ## _afh __attribute__ ((weak)) \
+ = {.get_file_info = NULL};
+ALL_AUDIO_FORMATS
+#undef AUDIO_FORMAT
+#define AUDIO_FORMAT(_fmt) & _fmt ## _afh,
+static struct audio_format_handler *afl[] = {ALL_AUDIO_FORMATS};
+#undef AUDIO_FORMAT
+#define NUM_AUDIO_FORMATS (ARRAY_SIZE(afl))
+/** \endcond audio_format_handler */
+
+/**
+ * Get the name of the given audio format.
*
- * We always define the full array of audio formats even if some audio formats
- * were not compiled in. This is because for each audio file the number of its
- * audio format is stored in the database. We don't want these numbers to become
- * stale just because the user installed a new version of paraslash that
- * supports a different set of audio formats.
+ * \param i The audio format number.
*
- * It can still be easily detected whether an audio format is compiled in by
- * checking if the init function pointer is not \p NULL.
+ * \return This returns a pointer to statically allocated memory so it
+ * must not be freed by the caller.
*/
-static struct audio_format_handler afl[] = {
- {
- .name = "mp3",
- .init = mp3_afh_init,
- },
- {
- .name = "ogg",
-#if defined(HAVE_OGG) && defined(HAVE_VORBIS)
- .init = ogg_afh_init,
-#endif
- },
- {
- .name = "aac",
-#if defined(HAVE_FAAD)
- .init = aac_afh_init,
-#endif
- },
- {
- .name = "wma",
- .init = wma_afh_init,
- },
- {
- .name = "spx",
-#if defined(HAVE_OGG) && defined(HAVE_SPEEX)
- .init = spx_afh_init,
-#endif
- },
- {
- .name = "flac",
-#if defined(HAVE_OGG) && defined(HAVE_FLAC)
- .init = flac_afh_init,
-#endif
- },
- {
- .name = "opus",
-#if defined(HAVE_OGG) && defined(HAVE_OPUS)
- .init = opus_afh_init,
-#endif
- },
- {
- .name = NULL,
- }
-};
+const char *audio_format_name(int i)
+{
+ if (i < 0 || i >= NUM_AUDIO_FORMATS)
+ return "???";
+ return audio_format_names[i];
+}
static inline int next_audio_format(int format)
{
for (;;) {
- if (!afl[format].name)
- return format;
format++;
- if (afl[format].init)
+ if (format >= NUM_AUDIO_FORMATS)
+ return format;
+ if (afl[format]->get_file_info)
return format;
}
}
/** Iterate over each supported audio format. */
-#define FOR_EACH_AUDIO_FORMAT(i) for (i = 0; afl[i].name; i = next_audio_format(i))
-
-/**
- * Call the init function of each supported audio format handler.
- */
-void afh_init(void)
-{
- int i;
-
- PARA_NOTICE_LOG("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
- FOR_EACH_AUDIO_FORMAT(i) {
- PARA_INFO_LOG("initializing %s handler\n",
- audio_format_name(i));
- afl[i].init(&afl[i]);
- }
-}
+#define FOR_EACH_AUDIO_FORMAT(i) \
+ for (i = 0; i < NUM_AUDIO_FORMATS; i = next_audio_format(i))
/**
* Tell whether an audio format handler provides chunk tables.
*/
bool afh_supports_dynamic_chunks(int audio_format_id)
{
- return afl[audio_format_id].get_chunk;
+ return afl[audio_format_id]->get_chunk;
}
/**
int i,j, len = strlen(name);
FOR_EACH_AUDIO_FORMAT(i) {
- for (j = 0; afl[i].suffixes[j]; j++) {
- const char *p = afl[i].suffixes[j];
+ for (j = 0; afl[i]->suffixes[j]; j++) {
+ const char *p = afl[i]->suffixes[j];
int plen = strlen(p);
if (len < plen + 1)
continue;
return -E_AUDIO_FORMAT;
}
-/**
- * Get the name of the given audio format.
- *
- * \param i The audio format number.
- *
- * \return This returns a pointer to statically allocated memory so it
- * must not be freed by the caller.
- */
-const char *audio_format_name(int i)
-{
- if (i < 0 || i >= ARRAY_SIZE(afl) - 1)
- return "???";
- return afl[i].name;
-}
-
static int get_file_info(int format, const char *path, char *data,
size_t size, int fd, struct afh_info *afhi)
{
const char *fmt = audio_format_name(format);
memset(afhi, 0, sizeof(*afhi));
- ret = afl[format].get_file_info(data, size, fd, afhi);
+ ret = afl[format]->get_file_info(data, size, fd, afhi);
if (ret < 0) {
PARA_WARNING_LOG("%s: %s format not detected: %s\n",
path, fmt, para_strerror(-ret));
uint8_t audio_format_id, const void *map, size_t mapsize,
const char **buf, size_t *len, void **afh_context)
{
- struct audio_format_handler *afh = afl + audio_format_id;
+ struct audio_format_handler *afh = afl[audio_format_id];
if (afh_supports_dynamic_chunks(audio_format_id)) {
int ret;
if (ret < 0)
return ret;
}
- ret = afl[audio_format_id].get_chunk(chunk_num, *afh_context,
+ ret = afh->get_chunk(chunk_num, *afh_context,
buf, len);
if (ret < 0) {
afh->close(*afh_context);
*/
void afh_close(void *afh_context, uint8_t audio_format_id)
{
- struct audio_format_handler *afh = afl + audio_format_id;
+ struct audio_format_handler *afh = afl[audio_format_id];
if (!afh_supports_dynamic_chunks(audio_format_id))
return;
void afh_get_header(struct afh_info *afhi, uint8_t audio_format_id,
void *map, size_t mapsize, char **buf, size_t *len)
{
- struct audio_format_handler *afh = afl + audio_format_id;
+ struct audio_format_handler *afh = afl[audio_format_id];
if (!map || !afhi || !afhi->header_len) {
*buf = NULL;
*/
void afh_free_header(char *header_buf, uint8_t audio_format_id)
{
- struct audio_format_handler *afh = afl + audio_format_id;
+ struct audio_format_handler *afh = afl[audio_format_id];
if (afh->get_header)
free(header_buf);
int afh_rewrite_tags(int audio_format_id, void *map, size_t mapsize,
struct taginfo *tags, int output_fd, const char *filename)
{
- struct audio_format_handler *afh = afl + audio_format_id;
+ struct audio_format_handler *afh = afl[audio_format_id];
if (!afh->rewrite_tags)
return -ERRNO_TO_PARA_ERROR(ENOTSUP);
return ret;
}
-/** See \ref recv_init(). */
const struct receiver lsg_recv_cmd_com_afh_user_data = {
- .init = afh_init,
.open = afh_recv_open,
.close = afh_recv_close,
.pre_select = afh_recv_pre_select,
{
uint32_t n;
- if (!afhi->chunk_table)
+ if (!afhi->chunk_table || afhi->chunks_total == 0)
return;
for (n = 0; n <= afhi->chunks_total; n++)
write_u32(buf + 4 * n, afhi->chunk_table[n]);
parse_config_or_die();
crypt_init();
daemon_set_priority(OPT_UINT32_VAL(PRIORITY));
- recv_init();
if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO,
COLOR_NO, OPT_GIVEN(LOGFILE))) {
for (i = 0; i < OPT_GIVEN(LOG_COLOR); i++)
static int com_ff(struct command_context *cc, struct lls_parse_result *lpr)
{
long promille;
- int ret, backwards = 0;
- unsigned i;
+ int i, ret;
char c, *errctx;
ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
send_errctx(cc, errctx);
return ret;
}
- if (!(ret = sscanf(lls_input(0, lpr), "%u%c", &i, &c)))
- return -E_COMMAND_SYNTAX;
- if (ret > 1 && c == '-')
- backwards = 1; /* jmp backwards */
+ ret = para_atoi32(lls_input(0, lpr), &i);
+ if (ret < 0) {
+ if (ret != -E_ATOI_JUNK_AT_END)
+ return ret;
+ /*
+ * Compatibility code to keep the historic syntax (ff 30-)
+ * working. This can be removed after 0.7.0.
+ */
+ ret = sscanf(lls_input(0, lpr), "%i%c", &i, &c);
+ if (ret <= 0)
+ return -E_COMMAND_SYNTAX;
+ if (ret > 1 && c == '-') {
+ PARA_WARNING_LOG("use of obsolete syntax\n");
+ i = -i;
+ }
+ }
mutex_lock(mmd_mutex);
ret = -E_NO_AUDIO_FILE;
if (!mmd->afd.afhi.chunks_total || !mmd->afd.afhi.seconds_total)
goto out;
ret = 1;
promille = (1000 * mmd->current_chunk) / mmd->afd.afhi.chunks_total;
- if (backwards)
- promille -= 1000 * i / mmd->afd.afhi.seconds_total;
- else
- promille += 1000 * i / mmd->afd.afhi.seconds_total;
+ /*
+ * We need this cast because without it the expression on the right
+ * hand side is of unsigned type.
+ */
+ promille += 1000 * i / (int)mmd->afd.afhi.seconds_total;
if (promille < 0)
promille = 0;
if (promille > 1000) {
size_t length, i;
int16_t *ip, *op;
uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr);
- unsigned gain_shift = inertia + U32_OPTVAL(DAMP, fn->lpr),
- mask = (1U << U32_OPTVAL(BLOCKSIZE, fn->lpr)) - 1U;
+ unsigned mask = (1U << U32_OPTVAL(BLOCKSIZE, fn->lpr)) - 1U;
//inplace = false;
next_buffer:
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
neg = true;
}
sample *= pcd->current_gain;
- sample >>= gain_shift;
+ sample >>= inertia + 1;
if (sample > 32767) { /* clip */
+ PARA_WARNING_LOG("clip: %d\n", sample);
sample = 32767;
pcd->current_gain = (3 * pcd->current_gain +
(1 << inertia)) / 4;
pcd->peak = 0;
} else if (sample > pcd->peak)
pcd->peak = sample;
+ sample >>= U32_OPTVAL(DAMP, fn->lpr);
op[i] = neg? -sample : sample;
if (++pcd->num_samples & mask)
continue;
// PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain,
// pcd->peak);
+
if (pcd->peak < U32_OPTVAL(TARGET_LEVEL, fn->lpr)) {
if (pcd->current_gain < pcd->max_gain)
pcd->current_gain++;
fn->private_data = pcd;
fn->min_iqs = 2; /* 16 bit audio */
pcd->current_gain = 1U << inertia;
- pcd->max_gain = 1U << (inertia + aggressiveness);
+ pcd->max_gain = (1U << inertia) * (1.0 + 3.0 * aggressiveness / 10.0);
+}
+
+static void *compress_setup(const struct lls_parse_result *lpr)
+{
+ uint32_t val;
+
+ val = U32_OPTVAL(BLOCKSIZE, lpr);
+ if (val == 0 || val > 31) {
+ PARA_EMERG_LOG("blocksize (%u) out of range\n", val);
+ exit(EXIT_FAILURE);
+ }
+ val = U32_OPTVAL(AGGRESSIVENESS, lpr);
+ if (val > 10) {
+ PARA_EMERG_LOG("aggressiveness (%u) out of range\n", val);
+ exit(EXIT_FAILURE);
+ }
+ val = U32_OPTVAL(INERTIA, lpr);
+ if (val == 0 || val > 14) {
+ PARA_EMERG_LOG("inertia (%u) out of range\n", val);
+ exit(EXIT_FAILURE);
+ }
+ val = U32_OPTVAL(TARGET_LEVEL, lpr);
+ if (val > 32767) {
+ PARA_EMERG_LOG("target-level (%u) out of range\n", val);
+ exit(EXIT_FAILURE);
+ }
+ val = U32_OPTVAL(DAMP, lpr);
+ if (val > 16) {
+ PARA_EMERG_LOG("damp (%u) out of range\n", val);
+ exit(EXIT_FAILURE);
+ }
+ return NULL; /* no need for a config structure */
}
const struct filter lsg_filter_cmd_com_compress_user_data = {
+ .setup = compress_setup,
.open = compress_open,
.close = compress_close,
.pre_select = generic_filter_pre_select,
return ret;
}
-/** See \ref recv_init(). */
const struct receiver lsg_recv_cmd_com_dccp_user_data = {
.open = dccp_recv_open,
.close = dccp_recv_close,
*/
#define SYSTEM_ERROR_BIT 30
-/**
- * Like the SYSTEM_ERROR_BIT, but indicates an error from the osl library.
- */
+/** Like SYSTEM_ERROR_BIT, but for errors from the osl library. */
#define OSL_ERROR_BIT 29
+/** Like SYSTEM_ERROR_BIT, but for errors from the lopsub library. */
#define LLS_ERROR_BIT 28
/** Check whether the system error bit is set. */
static const char * const flac_suffixes[] = {"flac", NULL};
/**
- * The init function of the flac audio format handler.
+ * The audio format handler for flac (free lossless audio decoder).
*
- * \param afh pointer to the struct to initialize
+ * It depends on libflac and on libogg.
*/
-void flac_afh_init(struct audio_format_handler *afh)
-{
- afh->get_file_info = flac_get_file_info,
- afh->suffixes = flac_suffixes;
- afh->rewrite_tags = flac_rewrite_tags;
-}
+const struct audio_format_handler flac_afh = {
+ .get_file_info = flac_get_file_info,
+ .suffixes = flac_suffixes,
+ .rewrite_tags = flac_rewrite_tags,
+};
return 1;
}
-/** See \ref recv_init(). */
const struct receiver lsg_recv_cmd_com_http_user_data = {
.open = http_recv_open,
.close = http_recv_close,
ci.argc = create_argv(ci.buffer, " ", &ci.argv);
ci.word_num = compute_word_num(ci.buffer, " ", ci.point);
+ /* determine the current word to complete */
end = ci.buffer + ci.point;
+
+ if (*end == ' ') {
+ if (ci.point == 0 || ci.buffer[ci.point - 1] == ' ') {
+ ci.word = para_strdup(NULL);
+ goto create_matches;
+ } else /* The cursor is positioned right after a word */
+ end--;
+ }
for (p = end; p > ci.buffer && *p != ' '; p--)
; /* nothing */
if (*p == ' ')
p++;
-
n = end - p + 1;
ci.word = para_malloc(n + 1);
strncpy(ci.word, p, n);
ci.word[n] = '\0';
-
+create_matches:
PARA_DEBUG_LOG("line: %s, point: %d (%c), wordnum: %d, word: %s\n",
ci.buffer, ci.point, ci.buffer[ci.point], ci.word_num, ci.word);
if (ci.word_num == 0)
#include "ipc.h"
#include <sys/types.h>
#include <sys/param.h>
-#include <sys/sysctl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
}
# if defined __FreeBSD__ || defined __NetBSD__
+#include <sys/sysctl.h>
# define SYSCTL_SHMMAX_VARIABLE "kern.ipc.shmmax"
-# elif defined __APPLE__
-# define SYSCTL_SHMMAX_VARIABLE "kern.sysv.shmmax"
# else
# undef SYSCTL_SHMMAX_VARIABLE
# endif
The backup suffix is '~'. That is, a single tilde character is appended
to the given file name.
[/help]
+ [option preserve]
+ summary = preserve modification time
+ [help]
+ If this option is given, the mtime of the modified file is set to
+ the value prior to the modification.
+ [/help]
[option year]
short_opt = y
summary = set the year tag
purpose = dynamically adjust the volume of an audio stream
[option blocksize]
short_opt = b
- summary = use blocks of size 2**bits
+ summary = adjust volume after each block of size 2**bits (1-31)
typestr = bits
arg_info = required_arg
arg_type = uint32
[/help]
[option aggressiveness]
short_opt = a
- summary = controls the maximum amount to amplify by
+ summary = controls the maximum amount to amplify by (0-10)
typestr = bits
arg_info = required_arg
arg_type = uint32
default_val = 4
+ [help]
+ This controls the maximal gain factor. Zero means to not amplify
+ at all while the value 10 corresponds to maximal gain factor which
+ results in a 4-fold increase in volume.
+ [/help]
[option inertia]
short_opt = i
- summary = how much inertia ramping has
+ summary = how much inertia ramping has (1-14)
typestr = bits
arg_info = required_arg
arg_type = uint32
default_val = 6
+ [help]
+ Larger values cause smaller volume adjustments.
+ [/help]
[option target-level]
short_opt = t
- summary = target signal level (0-32768)
+ summary = target signal level (0-32767)
typestr = level
arg_info = required_arg
arg_type = uint32
- default_val = 20000
+ default_val = 16384
+ [help]
+ If the peak of the previous block is less than the target level,
+ volume is increased slightly for the next block. Otherwise it is
+ decreased. The default value is chosen to minimize clipping. There
+ is usually no reason to change it.
+ [/help]
[option damp]
short_opt = d
- summary = if non-zero, scale down after normalizing
+ summary = if non-zero, scale down after normalizing (0-16)
typestr = bits
arg_info = required_arg
arg_type = uint32
default_val = 0
+ [help]
+ This scales down the volume of the audio stream by factor 2**bits.
+ This is mostly useful if another audio application (e.g., a video
+ game) is running in parallel and the relative volume of the audio
+ stream is too high.
+ [/help]
[subcommand fecdec]
purpose = decode a (lossy) input stream using forward error correction
[subcommand flacdec]
lls_m4_include_dir := $(lls_m4_dir)/include
$(lls_suite_dir)/%.m4d: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir)
- @[ -z "$(Q)" ] || echo 'M4D $<'
- $(Q) $(M4) -Pg -I $(lls_m4_include_dir) -s $< \
+ $(call SAY, M4D $<)
+ $(M4) -Pg -I $(lls_m4_include_dir) -s $< \
| awk '{if ($$1 ~ /#line/) {gsub(/"/, "", $$3); if ($$3 != "$<") \
print "$(lls_suite_dir)/$(*F).suite: " $$3}}' | sort | uniq > $@
$(lls_suite_dir)/%.suite: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir)
- @[ -z "$(Q)" ] || echo 'M4 $<'
- $(Q) $(M4) -Pg -I $(lls_m4_include_dir) -D GIT_VERSION=$(GIT_VERSION) \
+ $(call SAY, M4 $<)
+ $(M4) -Pg -I $(lls_m4_include_dir) -D GIT_VERSION=$(GIT_VERSION) \
-D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) -D LOGLEVELS=$(LOGLEVELS) \
$< > $@
$(lls_suite_dir)/%.lsg.c: $(lls_suite_dir)/%.suite
- @[ -z "$(Q)" ] || echo 'LSGC $<'
- $(Q) $(LOPSUBGEN) --gen-c --output-dir $(lls_suite_dir) < $<
+ $(call SAY, LSGC $<)
+ $(LOPSUBGEN) --gen-c --output-dir $(lls_suite_dir) < $<
$(lls_suite_dir)/%.lsg.h: $(lls_suite_dir)/%.suite
- @[ -z "$(Q)" ] || echo 'LSGH $<'
- $(Q) $(LOPSUBGEN) --gen-header --output-dir $(lls_suite_dir) < $<
+ $(call SAY, LSGH $<)
+ $(LOPSUBGEN) --gen-header --output-dir $(lls_suite_dir) < $<
$(lls_suite_dir)/%.lsg.man: $(lls_suite_dir)/%.suite
- @[ -z "$(Q)" ] || echo 'LSGM $<'
- $(Q) $(LOPSUBGEN) --gen-man --output-dir $(lls_suite_dir) < $<
+ $(call SAY, LSGM $<)
+ $(LOPSUBGEN) --gen-man --output-dir $(lls_suite_dir) < $<
$(object_dir)/%.o: $(lls_suite_dir)/%.c | $(object_dir)
- @[ -z "$(Q)" ] || echo 'CC $<'
- $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(STRICT_CFLAGS) $<
+ $(call SAY, CC $<)
+ $(CC) -c -o $@ $(CPPFLAGS) $(STRICT_CFLAGS) $<
summary = enable verbose mode
[subcommand ff]
- purpose = jump N seconds forward or backward
- synopsis = n[-]
+ purpose = jump forward or backward in the current audio file
+ synopsis = seconds
aux_info = VSS_READ | VSS_WRITE
[description]
- This sets the 'R' (reposition request) bit of the vss status flags
- which enqueues a request to jump n seconds forwards or backwards.
-
- Example:
-
- para_client ff 30-
-
- jumps 30 seconds backwards.
+ This enqueues a request to reposition the audio stream according to
+ the argument, which may be a signed or an unsigned integer. Negative
+ values correspond to backward jumps.
+
+ If a negative number is given whose absolute value exceeds the current
+ postition of the stream, a jump to the beginning of the audio file
+ is performed. If a positive amount of seconds is given which exceeds
+ the remaining time of the audio file, the next audio file is loaded.
[/description]
client_cmd("stop");
if (!fit || !fi_mood) /* nothing to do */
return 1;
- change_afs_mode(fi_mood);
for (;;) {
time(&t1);
if (wake_time_epoch <= t1 + fit)
(delay % 3600) / 60);
sleep(delay);
}
- client_cmd("play");
+ change_afs_mode(fi_mood);
+ if (sleep_mood) /* currently playing */
+ client_cmd("next");
+ else /* currently stopped */
+ client_cmd("play");
ret = fade(m, h, fiv, fit);
PARA_INFO_LOG("fade complete, returning\n");
return ret;
static const char * const mp3_suffixes[] = {"mp3", NULL};
/**
- * the init function of the mp3 audio format handler
+ * The mp3 audio format handler.
*
- * \param afh pointer to the struct to initialize
+ * It does not depend on any libraries and is hence always compiled in.
*/
-void mp3_afh_init(struct audio_format_handler *afh)
-{
- afh->get_file_info = mp3_get_file_info;
- afh->suffixes = mp3_suffixes;
+const struct audio_format_handler mp3_afh = {
+ .get_file_info = mp3_get_file_info,
+ .suffixes = mp3_suffixes,
#ifdef HAVE_LIBID3TAG
- afh->rewrite_tags = mp3_rewrite_tags;
+ .rewrite_tags = mp3_rewrite_tags,
#endif /* HAVE_LIBID3TAG */
-}
+};
static const char * const ogg_suffixes[] = {"ogg", NULL};
/**
- * The init function of the ogg vorbis audio format handler.
+ * The ogg vorbis audio format handler.
*
- * \param afh Pointer to the struct to initialize.
+ * It should actually be called vorbis because the ogg container format is also
+ * employed for the speex and flac codecs. Both the ogg and the vorbis
+ * libraries must be installed to get this audio format handler.
*/
-void ogg_afh_init(struct audio_format_handler *afh)
-{
- afh->get_file_info = ogg_vorbis_get_file_info;
- afh->get_header = vorbis_get_header;
- afh->suffixes = ogg_suffixes;
- afh->rewrite_tags = vorbis_rewrite_tags;
-}
+const struct audio_format_handler ogg_afh = {
+ .get_file_info = ogg_vorbis_get_file_info,
+ .get_header = vorbis_get_header,
+ .suffixes = ogg_suffixes,
+ .rewrite_tags = vorbis_rewrite_tags
+};
}
/**
- * The init function of the ogg/opus audio format handler.
+ * The audio format handler for ogg/opus.
*
- * \param afh Pointer to the struct to initialize.
+ * The opus codec was standardized by the Internet Engineering Task Force and
+ * is described in RFC 6716 (2012). The audio format handler depends on the ogg
+ * and the opus libraries.
*/
-void opus_afh_init(struct audio_format_handler *afh)
-{
- afh->get_file_info = opus_get_file_info,
- afh->get_header = opus_get_header;
- afh->suffixes = opus_suffixes;
- afh->rewrite_tags = opus_rewrite_tags;
-}
+const struct audio_format_handler opus_afh = {
+ .get_file_info = opus_get_file_info,
+ .get_header = opus_get_header,
+ .suffixes = opus_suffixes,
+ .rewrite_tags = opus_rewrite_tags,
+};
int ret;
unsigned num_inputs;
- /* needed this early to make help work */
- recv_init();
-
sched.default_timeout.tv_sec = 5;
parse_config_or_die(argc, argv);
- AFH_RECV->init();
session_open();
num_inputs = lls_num_inputs(play_lpr);
init_shuffle_map();
loglevel = OPT_UINT32_VAL(LOGLEVEL, lpr);
version_handle_flag("recv", OPT_GIVEN(VERSION, lpr));
handle_help_flag(lpr);
- recv_init();
memset(&rn, 0, sizeof(struct receiver_node));
ret = check_receiver_arg(OPT_STRING_VAL(RECEIVER, lpr), &receiver_lpr);
if (ret < 0)
* \sa \ref http_recv.c, \ref udp_recv.c.
*/
struct receiver {
- /**
- * The optional receiver init function.
- *
- * Performs any initialization needed before the receiver can be opened.
- */
- void (*init)(void);
/**
* Open one instance of the receiver.
*
/** Iterate over all available receivers. */
#define FOR_EACH_RECEIVER(i) for (i = 1; lls_cmd(i, recv_cmd_suite); i++)
-void recv_init(void);
int check_receiver_arg(const char *ra, struct lls_parse_result **lprp);
void print_receiver_helps(bool detailed);
int generic_recv_pre_select(struct sched *s, struct receiver_node *rn);
#include "recv.h"
#include "string.h"
-/**
- * Call the init function of each paraslash receiver.
- *
- * Receivers employ the user_data feature of the lopsub library: Each receiver
- * of the recv_cmd suite defines a struct receiver as its user data.
- * recv_init() obtains a pointer to this structure by calling lls_user_data().
- * If the receiver has an init function (i.e., if ->init is not NULL), ->init()
- * is called to initialize the receiver.
- */
-void recv_init(void)
-{
- int i;
-
- FOR_EACH_RECEIVER(i) {
- const struct lls_command *cmd = RECV_CMD(i);
- const struct receiver *r = lls_user_data(cmd);
- if (r && r->init)
- r->init();
- }
-}
-
/**
* Check if the given string is a valid receiver specifier.
*
/** \file send.h Sender-related defines and structures. */
+/**
+ * A little preprocessor fu helps to create the sender_subcommand enumeration
+ * below and the list of sender name strings without duplicating the commands.
+ */
#define SENDER_SUBCOMMANDS \
SENDER_SUBCOMMAND(add) /**< Add a target (udp only). */ \
SENDER_SUBCOMMAND(delete) /**< Delete a target (udp only). */ \
SENDER_SUBCOMMAND(on) /**< Activate the sender. */ \
SENDER_SUBCOMMAND(off) /**< Deactivate the sender. */ \
+/** Concatenate "SENDER_" and the given arg and append a comma. */
#define SENDER_SUBCOMMAND(_name) SENDER_ ## _name,
+
+/**
+ * Each sender subcommand gets an SENDER_xxx identifier. The identifier is
+ * passed from the sender command handler to the server process via shared
+ * memory.
+ */
enum sender_subcommand {
- SENDER_SUBCOMMANDS
+ SENDER_SUBCOMMANDS /**< List of SENDER_xxx identifiers. */
NUM_SENDER_CMDS /**< Used as array size in struct \ref sender. */
};
+
#undef SENDER_SUBCOMMAND
+
+/**
+ * Redefine it to expand to the stringified name of the sender so that
+ * SENDER_SUBCOMMANDS above now expands to the comma-separated list of sender
+ * name strings. This is used in command.c to define and initialize an array of
+ * char pointers.
+ */
#define SENDER_SUBCOMMAND(_name) #_name,
/**
init_ipc_or_die(); /* init mmd struct, mmd and log mutex */
daemon_set_start_time();
daemon_set_hooks(pre_log_hook, post_log_hook);
- PARA_NOTICE_LOG("initializing audio format handlers\n");
- afh_init();
-
/*
* Although afs uses its own signal handling we must ignore SIGUSR1
* _before_ the afs child process gets born by init_afs() below. It's
static const char * const speex_suffixes[] = {"spx", "speex", NULL};
/**
- * The init function of the ogg/speex audio format handler.
+ * The ogg/speex audio format handler.
*
- * \param afh Pointer to the struct to initialize.
+ * This codec is considered obsolete because the opus codec surpasses its
+ * performance in all areas. It is only compiled in if both the ogg and the
+ * speex library are installed.
*/
-void spx_afh_init(struct audio_format_handler *afh)
-{
- afh->get_file_info = spx_get_file_info,
- afh->suffixes = speex_suffixes;
- afh->rewrite_tags = spx_rewrite_tags;
-}
+const struct audio_format_handler spx_afh = {
+ .get_file_info = spx_get_file_info,
+ .suffixes = speex_suffixes,
+ .rewrite_tags = spx_rewrite_tags,
+};
}
/**
- * Free the content of a pointer and set it to \p NULL.
+ * Free the content of a pointer and set it to NULL.
*
- * This is equivalent to "free(*arg); *arg = NULL;".
+ * \param arg A pointer to the pointer whose content should be freed.
*
- * \param arg The pointer whose content should be freed.
+ * If arg is NULL, the function returns immediately. Otherwise it frees the
+ * memory pointed to by *arg and sets *arg to NULL. Hence callers have to pass
+ * the *address* of the pointer variable that points to the memory which should
+ * be freed.
*/
void freep(void *arg)
{
- void **ptr = (void **)arg;
- free(*ptr);
- *ptr = NULL;
+ if (arg) {
+ void **ptr = arg;
+ free(*ptr);
+ *ptr = NULL;
+ }
}
/**
test: $(tests)
$(tests): all
- $(Q) $@ $(test_options)
+ $(call SAY, $(@))
+ $@ $(test_options)
test-help:
- $(Q) for t in $(tests); do $$t $(test_options) -h; done
+ @for t in $(tests); do $$t $(test_options) -h; done
test-clean:
$(RM) -r $(results_dir)
return ret;
}
-/** See \ref recv_init(). */
const struct receiver lsg_recv_cmd_com_udp_user_data = {
.open = udp_recv_open,
.close = udp_recv_close,
static const char * const wma_suffixes[] = {"wma", NULL};
/**
- * The init function of the wma audio format handler.
+ * The audio format handler for Windows Media Audio.
*
- * \param afh Pointer to the struct to initialize.
+ * Only WMA version 2 is supported. This audio format handler does not depend
+ * on any third party libraries and is therefore always compiled in.
*/
-void wma_afh_init(struct audio_format_handler *afh)
-{
- afh->get_file_info = wma_get_file_info;
- afh->suffixes = wma_suffixes;
- afh->rewrite_tags = wma_rewrite_tags;
-}
+const struct audio_format_handler wma_afh = {
+ .get_file_info = wma_get_file_info,
+ .suffixes = wma_suffixes,
+ .rewrite_tags = wma_rewrite_tags,
+};