From: Andre Noll Date: Sun, 8 Sep 2019 09:07:45 +0000 (+0200) Subject: Merge branch 'refs/heads/t/ONESHELL' X-Git-Tag: v0.6.3~39 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=5459ae27b1efa26a1a47186c754a0e4cb486a278;hp=ece3b9ff875a28473a4f840bb016f8debb2531fc Merge branch 'refs/heads/t/ONESHELL' A single patch which instructs the build system to run multi-line statements in Makefiles in a single shell instance. Cooking in next for nine months. * refs/heads/t/ONESHELL: build: Use .ONESHELL. --- diff --git a/Makefile.real b/Makefile.real index c8d30aae..7d4eff4f 100644 --- a/Makefile.real +++ b/Makefile.real @@ -20,7 +20,7 @@ uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS") uname_rs := $(shell uname -rs) cc_version := $(shell $(CC) --version | head -n 1) GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h) -COPYRIGHT_YEAR := 2018 +COPYRIGHT_YEAR := 2019 ifeq ("$(origin O)", "command line") build_dir := $(O) @@ -112,7 +112,6 @@ CPPFLAGS += -DBUILD_DATE='"$(build_date)"' CPPFLAGS += -DLOGLEVELS='$(LOGLEVELS)' CPPFLAGS += -DUNAME_RS='"$(uname_rs)"' CPPFLAGS += -DCC_VERSION='"$(cc_version)"' -CPPFLAGS += -I/usr/local/include CPPFLAGS += -I$(lls_suite_dir) CPPFLAGS += -I$(yy_build_dir) CPPFLAGS += $(lopsub_cppflags) @@ -193,7 +192,7 @@ $(object_dir)/spx%.o: CPPFLAGS += $(speex_cppflags) $(object_dir)/flac%.o: CPPFLAGS += $(flac_cppflags) $(object_dir)/mp3_afh.o: CPPFLAGS += $(id3tag_cppflags) -$(object_dir)/crypt.o: CPPFLAGS += $(openssl_cppflags) +$(object_dir)/openssl.o: CPPFLAGS += $(openssl_cppflags) $(object_dir)/gcrypt.o: CPPFLAGS += $(gcrypt_cppflags) $(object_dir)/ao_write.o: CPPFLAGS += $(ao_cppflags) $(object_dir)/alsa%.o: CPPFLAGS += $(alsa_cppflags) diff --git a/NEWS.md b/NEWS.md index 55ef3be9..bbe20010 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,24 @@ 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. + -------------------------------------- 0.6.2 (2018-06-30) "elastic diversity" -------------------------------------- diff --git a/aac_afh.c b/aac_afh.c index 3315a42d..7f2a22a2 100644 --- a/aac_afh.c +++ b/aac_afh.c @@ -333,17 +333,17 @@ close: } 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, +}; diff --git a/afh.c b/afh.c index aa6570e1..567b560a 100644 --- a/afh.c +++ b/afh.c @@ -113,6 +113,24 @@ static int rewrite_tags(const char *name, int input_fd, void *map, 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 */ @@ -203,7 +221,6 @@ int main(int argc, char **argv) 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); diff --git a/afh.h b/afh.h index d2ac93ae..881db3c2 100644 --- a/afh.h +++ b/afh.h @@ -80,14 +80,6 @@ struct audio_file_data { * 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; /** @@ -135,7 +127,6 @@ struct audio_format_handler { 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); diff --git a/afh_common.c b/afh_common.c index bab4d415..a267f58b 100644 --- a/afh_common.c +++ b/afh_common.c @@ -11,108 +11,69 @@ #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. @@ -127,7 +88,7 @@ void afh_init(void) */ bool afh_supports_dynamic_chunks(int audio_format_id) { - return afl[audio_format_id].get_chunk; + return afl[audio_format_id]->get_chunk; } /** @@ -144,8 +105,8 @@ int guess_audio_format(const char *name) 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; @@ -160,21 +121,6 @@ int guess_audio_format(const char *name) 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) { @@ -182,7 +128,7 @@ static int get_file_info(int format, const char *path, char *data, 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)); @@ -303,7 +249,7 @@ __must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi, 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; @@ -313,7 +259,7 @@ __must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi, 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); @@ -340,7 +286,7 @@ __must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi, */ 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; @@ -403,7 +349,7 @@ int32_t afh_get_start_chunk(int32_t approx_chunk_num, 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; @@ -426,7 +372,7 @@ void afh_get_header(struct afh_info *afhi, uint8_t audio_format_id, */ 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); @@ -530,7 +476,7 @@ void set_max_chunk_size(struct afh_info *afhi) 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); diff --git a/afh_recv.c b/afh_recv.c index 6525209b..4f8ff497 100644 --- a/afh_recv.c +++ b/afh_recv.c @@ -238,9 +238,7 @@ out: 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, diff --git a/afs.c b/afs.c index 4fe2140b..d9cddeb8 100644 --- a/afs.c +++ b/afs.c @@ -589,8 +589,9 @@ static int com_select_callback(struct afs_callback_arg *aca) para_printf(&aca->pbout, "activating dummy mood\n"); activate_mood_or_playlist(NULL, &num_admissible, NULL); out: - para_printf(&aca->pbout, "activated %s (%d admissible files)\n", - current_mop? current_mop : "dummy mood", num_admissible); + para_printf(&aca->pbout, "activated %s (%d admissible file%s)\n", + current_mop? current_mop : "dummy mood", num_admissible, + num_admissible == 1? "" : "s"); free_lpr: lls_free_parse_result(aca->lpr, cmd); return ret; diff --git a/afs.h b/afs.h index b0d283f6..cfa9cc6d 100644 --- a/afs.h +++ b/afs.h @@ -285,10 +285,12 @@ int playlist_check_callback(struct afs_callback_arg *aca); struct para_buffer *pb, void *data); \ extern struct osl_table *table_name ## _table; +/** \cond blob_symbols */ DECLARE_BLOB_SYMBOLS(lyrics, lyr); DECLARE_BLOB_SYMBOLS(images, img); DECLARE_BLOB_SYMBOLS(moods, mood); DECLARE_BLOB_SYMBOLS(playlists, pl); +/** \endcond blob_symbols */ /** The columns of an abstract blob table. */ enum blob_table_columns { diff --git a/aft.c b/aft.c index 0db2c580..c04d4f9c 100644 --- a/aft.c +++ b/aft.c @@ -396,7 +396,7 @@ static void save_chunk_table(struct afh_info *afhi, char *buf) { 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]); diff --git a/audiod.c b/audiod.c index b93f29de..a5a77437 100644 --- a/audiod.c +++ b/audiod.c @@ -1451,7 +1451,6 @@ int main(int argc, char *argv[]) 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++) diff --git a/command.c b/command.c index 67f6bf3c..481fd0f6 100644 --- a/command.c +++ b/command.c @@ -663,8 +663,7 @@ EXPORT_SERVER_CMD_HANDLER(nomore); 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)); @@ -672,20 +671,33 @@ static int com_ff(struct command_context *cc, struct lls_parse_result *lpr) 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) { diff --git a/compress_filter.c b/compress_filter.c index 69a982ef..15bed6df 100644 --- a/compress_filter.c +++ b/compress_filter.c @@ -48,8 +48,7 @@ static int compress_post_select(__a_unused struct sched *s, void *context) 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); @@ -78,19 +77,22 @@ next_buffer: 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++; @@ -121,10 +123,43 @@ static void compress_open(struct filter_node *fn) 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, diff --git a/daemon.c b/daemon.c index bfa81487..a4e2f319 100644 --- a/daemon.c +++ b/daemon.c @@ -146,6 +146,22 @@ void daemon_set_loglevel(const char *loglevel) me->loglevel = ret; } +/** + * Register functions to be called before and after a message is logged. + * + * \param pre_log_hook Called before the message is logged. + * \param post_log_hook Called after the message is logged. + * + * The purpose of this function is to provide a primitive for multi-threaded + * applications to serialize the access to the log facility, preventing + * interleaving log messages. This can be achieved by having the pre-log hook + * acquire a lock which blocks the other threads on the attempt to log a + * message at the same time. The post-log hook is responsible for releasing + * the lock. + * + * If these hooks are unnecessary, for example because the application is + * single-threaded, this function does not need to be called. + */ void daemon_set_hooks(void (*pre_log_hook)(void), void (*post_log_hook)(void)) { me->pre_log_hook = pre_log_hook; diff --git a/dccp_recv.c b/dccp_recv.c index 8a08504a..639c93fc 100644 --- a/dccp_recv.c +++ b/dccp_recv.c @@ -151,7 +151,6 @@ out: return ret; } -/** See \ref recv_init(). */ const struct receiver lsg_recv_cmd_com_dccp_user_data = { .open = dccp_recv_open, .close = dccp_recv_close, diff --git a/error.h b/error.h index c06d316d..8f4f9bb1 100644 --- a/error.h +++ b/error.h @@ -254,8 +254,9 @@ enum para_error_codes {PARA_ERRORS}; #undef PARA_ERROR #define PARA_ERROR(err, msg) msg -/** Array of error strings. */ +/** All .c files need the declararation of the array of error strings. */ extern const char * const para_errlist[]; +/** Exactly one .c file per executable must define the array. */ #define DEFINE_PARA_ERRLIST const char * const para_errlist[] = {PARA_ERRORS} /** diff --git a/flac_afh.c b/flac_afh.c index 45373ea1..6e236839 100644 --- a/flac_afh.c +++ b/flac_afh.c @@ -515,13 +515,12 @@ free_pfad: 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, +}; diff --git a/http_recv.c b/http_recv.c index 3a723299..1fb60bad 100644 --- a/http_recv.c +++ b/http_recv.c @@ -167,7 +167,6 @@ static int http_recv_open(struct receiver_node *rn) return 1; } -/** See \ref recv_init(). */ const struct receiver lsg_recv_cmd_com_http_user_data = { .open = http_recv_open, .close = http_recv_close, diff --git a/interactive.c b/interactive.c index 190cdf29..a8197308 100644 --- a/interactive.c +++ b/interactive.c @@ -762,17 +762,25 @@ int i9e_print_completions(struct i9e_completer *completers) 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) diff --git a/ipc.c b/ipc.c index 85013a63..8e9dd51a 100644 --- a/ipc.c +++ b/ipc.c @@ -7,7 +7,6 @@ #include "ipc.h" #include #include -#include #include #include @@ -194,9 +193,8 @@ int shm_detach(void *addr) } # if defined __FreeBSD__ || defined __NetBSD__ +#include # define SYSCTL_SHMMAX_VARIABLE "kern.ipc.shmmax" -# elif defined __APPLE__ -# define SYSCTL_SHMMAX_VARIABLE "kern.sysv.shmmax" # else # undef SYSCTL_SHMMAX_VARIABLE # endif diff --git a/m4/lls/afh.suite.m4 b/m4/lls/afh.suite.m4 index cf83b972..7ebf208c 100644 --- a/m4/lls/afh.suite.m4 +++ b/m4/lls/afh.suite.m4 @@ -62,6 +62,12 @@ version-string = GIT_VERSION() 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 diff --git a/m4/lls/filter_cmd.suite.m4 b/m4/lls/filter_cmd.suite.m4 index d269d237..c026a628 100644 --- a/m4/lls/filter_cmd.suite.m4 +++ b/m4/lls/filter_cmd.suite.m4 @@ -25,7 +25,7 @@ caption = filters 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 @@ -35,32 +35,52 @@ caption = filters [/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] diff --git a/m4/lls/play.suite.m4 b/m4/lls/play.suite.m4 index 4af2a05a..57a9377e 100644 --- a/m4/lls/play.suite.m4 +++ b/m4/lls/play.suite.m4 @@ -9,10 +9,11 @@ version-string = GIT_VERSION() [description] para_play operates either in command mode or in insert mode. In insert mode it presents a prompt and allows the user to enter commands like - stop, play, pause etc. In command mode the current audio file and the - playback position are shown and the program reads single key strokes - from stdin. Keys may be mapped to commands so that the configured - command is executed when a mapped key is pressed. + play, pause, quit, etc. In command mode the current audio file and the + playback position are shown instead of the prompt/command line, and + the program reads single key strokes from stdin. Keys may be mapped + to commands so that the configured command is executed whenever a + mapped key is pressed. [/description] m4_include(common-option-section.m4) m4_include(help.m4) diff --git a/m4/lls/server_cmd.suite.m4 b/m4/lls/server_cmd.suite.m4 index 079589d1..2a39907c 100644 --- a/m4/lls/server_cmd.suite.m4 +++ b/m4/lls/server_cmd.suite.m4 @@ -124,18 +124,18 @@ aux_info_prefix = Permissions: 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] diff --git a/mixer.c b/mixer.c index ad674bf5..efa42b93 100644 --- a/mixer.c +++ b/mixer.c @@ -1,6 +1,6 @@ /* Copyright (C) 1998 Andre Noll , see file COPYING. */ -/** \file mixer.c A volume fader and alarm clock for OSS. */ +/** \file mixer.c A volume fader and alarm clock. */ #include #include @@ -313,7 +313,6 @@ static int com_sleep(const struct mixer *m, struct mixer_handle *h) 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) @@ -324,7 +323,11 @@ static int com_sleep(const struct mixer *m, struct mixer_handle *h) (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; @@ -526,7 +529,7 @@ int main(int argc, char *argv[]) } if (ret < 0) goto close_mixer; - ret = (*(mixer_subcommand_handler_t *)(lls_user_data(cmd)))(m ,h); + ret = (*(mixer_subcommand_handler_t *)(lls_user_data(cmd)))(m, h); close_mixer: m->close(&h); free_sub_lpr: diff --git a/mp3_afh.c b/mp3_afh.c index 42dd7539..6ed73c2a 100644 --- a/mp3_afh.c +++ b/mp3_afh.c @@ -63,8 +63,6 @@ static const int mp3info_bitrate[2][3][14] = { }; static const int frame_size_index[] = {24000, 72000, 72000}; -static const char *mode_text[] = {"stereo", "joint stereo", "dual channel", "mono", "invalid"}; - #ifdef HAVE_ID3TAG #include @@ -433,10 +431,13 @@ static int header_frequency(struct mp3header *h) return frequencies[h->version][h->freq]; } -static const char *header_mode(struct mp3header *h) +static const char *header_mode(const struct mp3header *h) { - if (h->mode > 4) - h->mode = 4; /* invalid */ + const char * const mode_text[] = {"stereo", "joint stereo", + "dual channel", "mono"}; + + if (h->mode >= ARRAY_SIZE(mode_text)) + return "invalid"; return mode_text[h->mode]; } @@ -682,15 +683,14 @@ static int mp3_get_file_info(char *map, size_t numbytes, int fd, 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 */ -} +}; diff --git a/ogg_afh.c b/ogg_afh.c index 93ad14bb..5da339fe 100644 --- a/ogg_afh.c +++ b/ogg_afh.c @@ -173,14 +173,15 @@ static int vorbis_rewrite_tags(const char *map, size_t mapsize, 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 +}; diff --git a/opus_afh.c b/opus_afh.c index ed6fe5c8..dca6cfba 100644 --- a/opus_afh.c +++ b/opus_afh.c @@ -291,14 +291,15 @@ static void opus_get_header(void *map, size_t mapsize, char **buf, } /** - * 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, +}; diff --git a/play.c b/play.c index 647367a3..86edc4d4 100644 --- a/play.c +++ b/play.c @@ -1238,12 +1238,8 @@ int main(int argc, char *argv[]) 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(); diff --git a/recv.c b/recv.c index 7b46d78c..10d55d21 100644 --- a/recv.c +++ b/recv.c @@ -79,7 +79,6 @@ int main(int argc, char *argv[]) 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) diff --git a/recv.h b/recv.h index f88ebd84..36b0f1db 100644 --- a/recv.h +++ b/recv.h @@ -39,12 +39,6 @@ struct receiver_node { * \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. * @@ -114,7 +108,6 @@ struct 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); diff --git a/recv_common.c b/recv_common.c index 948de474..31fd81f1 100644 --- a/recv_common.c +++ b/recv_common.c @@ -15,27 +15,6 @@ #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. * diff --git a/server.c b/server.c index 6370cefd..e0d50f4f 100644 --- a/server.c +++ b/server.c @@ -583,9 +583,6 @@ static void server_init(int argc, char **argv, struct server_command_task *sct) 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 diff --git a/spx_afh.c b/spx_afh.c index e2323647..caeacb19 100644 --- a/spx_afh.c +++ b/spx_afh.c @@ -248,13 +248,14 @@ static int spx_rewrite_tags(const char *map, size_t mapsize, 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, +}; diff --git a/string.c b/string.c index 742eafdf..65d22408 100644 --- a/string.c +++ b/string.c @@ -113,9 +113,9 @@ __must_check __malloc char *para_strdup(const char *s) } /** - * Print a formated message to a dynamically allocated string. + * Print a formatted message to a dynamically allocated string. * - * \param result The formated string is returned here. + * \param result The formatted string is returned here. * \param fmt The format string. * \param ap Initialized list of arguments. * diff --git a/udp_recv.c b/udp_recv.c index 67846b14..58d45ab4 100644 --- a/udp_recv.c +++ b/udp_recv.c @@ -186,7 +186,6 @@ err: return ret; } -/** See \ref recv_init(). */ const struct receiver lsg_recv_cmd_com_udp_user_data = { .open = udp_recv_open, .close = udp_recv_close, diff --git a/vss.c b/vss.c index f6ec8393..73c72311 100644 --- a/vss.c +++ b/vss.c @@ -941,6 +941,7 @@ static int recv_afs_msg(int afs_socket, int *fd, uint32_t *code, uint32_t *data) } #ifndef MAP_POPULATE +/** As of 2018, neither FreeBSD-11.2 nor NetBSD-8.0 have MAP_POPULATE. */ #define MAP_POPULATE 0 #endif diff --git a/web/about.in.html b/web/about.in.html index d91a2ad4..8395864a 100644 --- a/web/about.in.html +++ b/web/about.in.html @@ -17,7 +17,8 @@ systems. It is written in C and released under the GPLv2. Author: André Noll, -maan@tuebingen.mpg.de +maan@tuebingen.mpg.de, +Homepage: http://people.tuebingen.mpg.de/maan/
Comments and bug reports are welcome. Please provide the version of paraslash you are using and relevant parts of the logs. diff --git a/web/manual.md b/web/manual.md index e0f5ccf1..2c806614 100644 --- a/web/manual.md +++ b/web/manual.md @@ -446,7 +446,7 @@ following commands: Next, change to the "bar" account on client_host and generate the key pair with the commands - ssh-keygen -q -t rsa -b 2048 -N '' -m PEM -f $key + ssh-keygen -q -t rsa -b 2048 -N '' -m PEM This generates the two files id_rsa and id_rsa.pub in ~/.ssh. Note that para_server won't accept keys shorter than 2048 bits. Moreover, diff --git a/wma_afh.c b/wma_afh.c index e6048a86..63e49677 100644 --- a/wma_afh.c +++ b/wma_afh.c @@ -646,13 +646,13 @@ out: 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, +};