Merge branch 'refs/heads/t/compress'
authorAndre Noll <maan@tuebingen.mpg.de>
Sat, 24 Aug 2019 11:38:29 +0000 (13:38 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Sat, 24 Aug 2019 11:39:16 +0000 (13:39 +0200)
A short series which overhauls the algorithm behind the compress
filter and its documentation.

Cooking for almost a year.

* refs/heads/t/compress:
  compress: Overhaul the meaning of --aggressiveness.
  compress: Apply damping later.
  compress: Warn when samples are clipped.
  compress: Document and sanity-check command line options.
  compress: Fix off by one in help of --target-level.

25 files changed:
Makefile.real
NEWS.md
afh.c
afs.c
afs.h
aft.c
ao_write.c
blob.c
check_wav.c
command.c
daemon.c
error.h
interactive.c
m4/lls/afh.suite.m4
m4/lls/play.suite.m4
m4/lls/server_cmd.suite.m4
m4/lls/write_cmd.suite.m4
mixer.c
mp3_afh.c
oss_write.c
spxdec_filter.c
string.c
vss.c
web/about.in.html
web/manual.md

index dae48f0e7f85f43984c2be17cdb26d4b8945032d..5df27bad934a58f04b4b6407e868e8898ded4002 100644 (file)
@@ -18,7 +18,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)
@@ -110,7 +110,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)
@@ -191,7 +190,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 55ef3be947ee76d6b0937b33988b0cd960c0e985..50ec0a3a51e156b704cf6979ec595f83a300192e 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,21 @@
 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.
+
 --------------------------------------
 0.6.2 (2018-06-30) "elastic diversity"
 --------------------------------------
diff --git a/afh.c b/afh.c
index aa6570e1abc33bfe38e9ce87bf87316707263473..ffb4315b0d41e7087aa03ee7b353e7f690989126 100644 (file)
--- 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 */
diff --git a/afs.c b/afs.c
index 4fe2140be607effea9d5c0a17c1eec8f41011b59..d9cddeb87b356103164bee70ddf2534122c7923a 100644 (file)
--- 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 b0d283f626af87321bc405bceef8fa50036a9a59..cfa9cc6df4c11900739e9ff90fba0c4b30bf16e8 100644 (file)
--- 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 5d4e64773e8a7e7011ef88bb69771a21bd285a1b..c04d4f9c99e89afb451afe94be7fcc64461b15e1 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -50,46 +50,28 @@ static struct ls_data status_item_ls_data;
 
 /** The different sorting methods of the ls command. */
 enum ls_sorting_method {
-       /** -sp (default) */
-       LS_SORT_BY_PATH,
-       /** -ss */
-       LS_SORT_BY_SCORE,
-       /** -sl */
-       LS_SORT_BY_LAST_PLAYED,
-       /** -sn */
-       LS_SORT_BY_NUM_PLAYED,
-       /** -sf */
-       LS_SORT_BY_FREQUENCY,
-       /** -sc */
-       LS_SORT_BY_CHANNELS,
-       /** -si */
-       LS_SORT_BY_IMAGE_ID,
-       /** -sy */
-       LS_SORT_BY_LYRICS_ID,
-       /** -sb */
-       LS_SORT_BY_BITRATE,
-       /** -sd */
-       LS_SORT_BY_DURATION,
-       /** -sa */
-       LS_SORT_BY_AUDIO_FORMAT,
-       /** -sh */
-       LS_SORT_BY_HASH,
+       LS_SORT_BY_PATH, /**< -s=p (default) */
+       LS_SORT_BY_SCORE, /**< -s=s */
+       LS_SORT_BY_LAST_PLAYED, /**< -s=l */
+       LS_SORT_BY_NUM_PLAYED, /**< -s=n */
+       LS_SORT_BY_FREQUENCY, /**< -s=f */
+       LS_SORT_BY_CHANNELS, /**< -s=c */
+       LS_SORT_BY_IMAGE_ID, /**< -s=i */
+       LS_SORT_BY_LYRICS_ID, /**< -s=y */
+       LS_SORT_BY_BITRATE, /**< -s=b */
+       LS_SORT_BY_DURATION, /**< -s=d */
+       LS_SORT_BY_AUDIO_FORMAT, /**< -s=a */
+       LS_SORT_BY_HASH, /**< -s=h */
 };
 
 /** The different listing modes of the ls command. */
 enum ls_listing_mode {
-       /** Default listing mode. */
-       LS_MODE_SHORT,
-       /** -l or -ll */
-       LS_MODE_LONG,
-       /** -lv */
-       LS_MODE_VERBOSE,
-       /** -lm */
-       LS_MODE_MBOX,
-       /** -lc */
-       LS_MODE_CHUNKS,
-       /** -lp */
-       LS_MODE_PARSER,
+       LS_MODE_SHORT, /**< Default listing mode. */
+       LS_MODE_LONG, /**< -l or -l=l */
+       LS_MODE_VERBOSE, /** -l=v */
+       LS_MODE_MBOX, /** -l=m */
+       LS_MODE_CHUNKS, /** -l=c */
+       LS_MODE_PARSER, /** -l=p */
 };
 
 /**
@@ -414,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]);
@@ -983,13 +965,18 @@ out:
        WRITE_STATUS_ITEM(pb, SI_file_size, "%ld\n", statbuf.st_size / 1024);
 }
 
+/**
+ * Deallocate and invalidate the status item strings.
+ *
+ * This needs to be a public function so that afs.c can call it on shutdown.
+ */
 void free_status_items(void)
 {
        freep(&status_items);
        freep(&parser_friendly_status_items);
 }
 
-static int make_status_items(void)
+static void make_status_items(void)
 {
        const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
        char *argv[] = {"ls", "--admissible", "--listing-mode=verbose"};
@@ -1000,7 +987,7 @@ static int make_status_items(void)
 
        free_status_items();
        if (!status_item_ls_data.path) /* no audio file open */
-               return 0;
+               return;
        ret = lls_parse(ARRAY_SIZE(argv), argv, cmd, &opts.lpr, NULL);
        assert(ret >= 0);
        time(&current_time);
@@ -1020,10 +1007,12 @@ static int make_status_items(void)
        parser_friendly_status_items = pb.buf;
        ret = 1;
 out:
-       if (ret < 0)
+       if (ret < 0) {
+               PARA_WARNING_LOG("could not create status items: %s\n",
+                       para_strerror(-ret));
                free_status_items();
+       }
        lls_free_parse_result(opts.lpr, cmd);
-       return ret;
 }
 
 /**
index e2858b918ebfd1d1e552147a4d62883990711a4c..447dea84c00a468330fe8229bfd70b74292264bd 100644 (file)
@@ -87,7 +87,7 @@ static int aow_set_sample_format(unsigned sample_rate, unsigned channels,
                case SF_U8:
                case SF_U16_LE:
                case SF_U16_BE:
-                       return -E_AO_BAD_SAMPLE_FORMAT;
+                       return -E_BAD_SAMPLE_FORMAT;
                case SF_S8:
                        /* no need to set byte_format */
                        result->bits = 8;
@@ -159,8 +159,8 @@ static void aow_show_drivers(void)
 
                if (info->type == AO_TYPE_FILE)
                        continue;
-               PARA_DEBUG_LOG("%s: %s", info->short_name, info->name);
-               PARA_DEBUG_LOG("priority: %d", info->priority);
+               PARA_DEBUG_LOG("name: %s: %s\n", info->short_name, info->name);
+               PARA_DEBUG_LOG("priority: %d\n", info->priority);
                for (j = 0; j < info->option_count; j++) {
                        tmp = make_message("%s%s%s", keys? keys : "",
                                keys? ", " : "",
@@ -168,9 +168,9 @@ static void aow_show_drivers(void)
                        free(keys);
                        keys = tmp;
                }
-               PARA_DEBUG_LOG("keys: %s", keys? keys : "[none]");
+               PARA_DEBUG_LOG("keys: %s\n", keys? keys : "[none]");
                free(keys);
-               PARA_DEBUG_LOG("comment: %s", info->comment?
+               PARA_DEBUG_LOG("comment: %s\n", info->comment?
                        info->comment : "[none]");
        }
 }
diff --git a/blob.c b/blob.c
index 890b92267ef68d390f8806a135c37a2cc9809021..4ecbc45bb15f42c15342e7900f4de94522ef45bb 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -72,7 +72,6 @@ static struct osl_column_description blob_cols[] = {
 /** Define a pointer to an osl blob table with a canonical name. */
 #define DEFINE_BLOB_TABLE_PTR(table_name) struct osl_table *table_name ## _table;
 
-
 /** Define a blob table. */
 #define INIT_BLOB_TABLE(table_name) \
        DEFINE_BLOB_TABLE_DESC(table_name); \
@@ -104,7 +103,7 @@ static int print_blob(struct osl_table *table, struct osl_row *row,
                para_printf(&aca->pbout, "cannot list %s\n", name);
                return ret;
        }
-       id = *(uint32_t *)obj.data;
+       id = read_u32(obj.data);
        para_printf(&aca->pbout, "%u\t%s\n", id, name);
        return 1;
 }
@@ -121,6 +120,7 @@ static int com_lsblob_callback(const struct lls_command * const cmd,
                .action = print_blob,
        };
        int ret;
+
        ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
        pmd.lpr = aca->lpr;
        assert(ret >= 0);
@@ -176,6 +176,7 @@ static int com_catblob_callback(const struct lls_command * const cmd,
                .data = &aca->fd,
                .action = cat_blob
        };
+
        ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
        assert(ret >= 0);
        pmd.lpr = aca->lpr;
@@ -207,6 +208,7 @@ static int remove_blob(struct osl_table *table, struct osl_row *row,
 {
        struct afs_callback_arg *aca = data;
        int ret = osl(osl_del_row(table, row));
+
        if (ret < 0) {
                para_printf(&aca->pbout, "cannot remove %s\n", name);
                return ret;
@@ -226,6 +228,7 @@ static int com_rmblob_callback(const struct lls_command * const cmd,
                .data = aca,
                .action = remove_blob
        };
+
        ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
        assert(ret >= 0);
        pmd.lpr = aca->lpr;
@@ -263,7 +266,8 @@ static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
        struct osl_object objs[NUM_BLOB_COLUMNS];
        char *name = aca->query.data;
        size_t name_len = strlen(name) + 1;
-       uint32_t id;
+       uint32_t id = (uint32_t)-1; /* STFU, gcc */
+       char id_buf[sizeof(id)];
        unsigned num_rows;
        int ret;
 
@@ -271,10 +275,15 @@ static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
        if (ret < 0)
                goto out;
        if (!num_rows) { /* this is the first entry ever added */
-               /* insert dummy row containing the id */
-               id = 2; /* this entry will be entry #1, so 2 is the next */
-               objs[BLOBCOL_ID].data = &id;
-               objs[BLOBCOL_ID].size = sizeof(id);
+               /*
+                * Insert dummy row containing the next free ID. Since we are
+                * about to insert the first blob with ID 1, the next free ID
+                * will be 2.
+                */
+               id = 2U;
+               write_u32(id_buf, id);
+               objs[BLOBCOL_ID].data = id_buf;
+               objs[BLOBCOL_ID].size = sizeof(id_buf);
                objs[BLOBCOL_NAME].data = "";
                objs[BLOBCOL_NAME].size = 1;
                objs[BLOBCOL_DEF].data = "";
@@ -293,7 +302,7 @@ static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
                        ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
                        if (ret < 0)
                                goto out;
-                       id = *(uint32_t *)obj.data;
+                       id = read_u32(obj.data);
                        obj.data = name + name_len;
                        obj.size = aca->query.size - name_len;
                        ret = osl(osl_update_object(table, row, BLOBCOL_DEF, &obj));
@@ -308,15 +317,17 @@ static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
                ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
                if (ret < 0)
                        goto out;
-               id = *(uint32_t *)obj.data + 1;
-               obj.data = &id;
+               id = read_u32(obj.data) + 1;
+               write_u32(id_buf, id);
+               obj.data = &id_buf;
                ret = osl(osl_update_object(table, row, BLOBCOL_ID, &obj));
                if (ret < 0)
                        goto out;
        }
        id--;
-       objs[BLOBCOL_ID].data = &id;
-       objs[BLOBCOL_ID].size = sizeof(id);
+       write_u32(id_buf, id);
+       objs[BLOBCOL_ID].data = &id_buf;
+       objs[BLOBCOL_ID].size = sizeof(id_buf);
        objs[BLOBCOL_NAME].data = name;
        objs[BLOBCOL_NAME].size = name_len;
        objs[BLOBCOL_DEF].data = name + name_len;
@@ -374,7 +385,7 @@ again:
  *
  * This function is called from the addblob command handlers to instruct the
  * afs process to store the input in a blob table. Input is read and decrypted
- * from the file descriptor given by cc and appended to arg_obj, which contains
+ * from the file descriptor given by cc and appended to a buffer which also contains
  * the name of the blob to create. The combined buffer is made available to the
  * afs process via the callback method.
  */
@@ -509,7 +520,6 @@ static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
                return blob_get_name_by_id(table_name ## _table, id, name); \
        }
 
-
 static int blob_get_def_by_name(struct osl_table *table, char *name,
                struct osl_object *def)
 {
@@ -561,6 +571,7 @@ static int blob_get_name_and_def_by_row(struct osl_table *table,
 {
        struct osl_object obj;
        int ret = osl(osl_get_object(table, row, BLOBCOL_NAME, &obj));
+
        if (ret < 0)
                return ret;
        *name = obj.data;
@@ -596,6 +607,7 @@ static int blob_open(struct osl_table **table,
                const char *dir)
 {
        int ret;
+
        desc->dir = dir;
        ret = osl(osl_open_table(desc, table));
        if (ret >= 0)
index f0990bf8fcde6b99388f46319402c00bb5c1143c..89ebdacc0805e9575acea92d09ed3b2dd81ebac3 100644 (file)
@@ -11,6 +11,7 @@
 #include "buffer_tree.h"
 #include "error.h"
 #include "check_wav.h"
+#include "portable_io.h"
 
 /** Length of a standard wav header. */
 #define WAV_HEADER_LEN 44
@@ -155,9 +156,9 @@ int check_wav_post_select(struct check_wav_context *cwc)
        PARA_INFO_LOG("found wav header\n");
        cwc->state = CWS_HAVE_HEADER;
        /* Only set those values which have not already been set. */
-       cwc->channels = (unsigned)a[22];
-       cwc->sample_rate = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24);
-       bps = a[34] + ((unsigned)a[35] << 8);
+       cwc->channels = a[22];
+       cwc->sample_rate = read_u32(a + 24);
+       bps = read_u16(a + 34);
        if (bps != 8 && bps != 16) {
                PARA_WARNING_LOG("%u bps not supported, assuming 16\n",
                        bps);
index 6adb945555d32e101fdd24bc7255133e33f0bd1b..481fd0f637352fb21ff97536d426ece42ec0fda3 100644 (file)
--- a/command.c
+++ b/command.c
@@ -279,7 +279,7 @@ static int check_sender_args(struct command_context *cc,
 }
 
 /**
- * Send a sideband packet through a blocking file descriptor.
+ * Receive a sideband packet from a blocking file descriptor.
  *
  * \param scc fd and crypto keys.
  * \param expected_band The expected band designator.
@@ -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) {
index bfa81487430f62dc7417db6ba6e0c6d53c008051..a4e2f3193730d456ce9908eb55888cdb759154e0 100644 (file)
--- 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/error.h b/error.h
index 02f42246c97c9fda934a3b675008566eec351bf3..8f4f9bb134027956fbd438cd102be3fe5d1bea7e 100644 (file)
--- a/error.h
+++ b/error.h
@@ -24,7 +24,6 @@
        PARA_ERROR(AO_APPEND_OPTION, "ao append option: memory allocation failure"), \
        PARA_ERROR(AO_BAD_DRIVER, "ao: invalid driver"), \
        PARA_ERROR(AO_BAD_OPTION, "ao option is not of type key:value"), \
-       PARA_ERROR(AO_BAD_SAMPLE_FORMAT, "ao: unsigned sample formats not supported"), \
        PARA_ERROR(AO_DEFAULT_DRIVER, "ao: no usable output device"), \
        PARA_ERROR(AO_EOF, "ao: end of file"), \
        PARA_ERROR(AO_FILE_NOT_SUPP, "ao: file io drivers not supported"), \
 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}
 
 /**
index 190cdf295130a2d711074e7c5f5927174b312076..a8197308e8323c8fe78546117cc2e9479df0fbfd 100644 (file)
@@ -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)
index cf83b9724d824079e720c1d1a357962c9cd6872a..7ebf208c01552e7b17bfd7a5fff37b6f90051c58 100644 (file)
@@ -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
index 4af2a05ab05226eefc81b8a9109fa364b436a663..57a9377eb19030cd9bbe1761d1b72b1dcce69aaa 100644 (file)
@@ -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)
index 079589d1f36eee98fb9a0c1252906b5d9b1e43a4..2a39907cee624219d1fb166b07dbf84288f67bb4 100644 (file)
@@ -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]
 
index 9d8ee75a5d85aad978f09ba5c6ac71c3bd8520d3..102afed1d19be39cfd2a719cbf5c243f40e419f4 100644 (file)
@@ -38,8 +38,9 @@ caption = writers
                arg_info = required_arg
                arg_type = string
                [help]
-                       If this is not given, the driver with the highest priority (see below)
-                       will be used.
+                       If this is not given, the driver with the highest priority will be
+                       used. The list of available drivers and their priorities is shown in
+                       debug mode.
                [/help]
        [option ao-option]
                short_opt = o
@@ -51,7 +52,7 @@ caption = writers
                [help]
                        For each time this option is given, the supplied key-value pair is
                        appended to the list of options for the driver. Invalid keys are
-                       silently ignored.
+                       silently ignored. The list of available keys is shown in debug mode.
                [/help]
 [subcommand oss]
        purpose = output plugin for the Open Sound System
@@ -62,18 +63,6 @@ caption = writers
                arg_info = required_arg
                arg_type = string
                default_val = /dev/dsp
-[subcommand osx]
-       purpose = output plugin for Mac OS coreaudio
-       [option numbuffers]
-               short_opt = n
-               summary = number of audio buffers to allocate
-               typestr = num
-               arg_info = required_arg
-               arg_type = uint32
-               default_val = 20
-               [help]
-                       Increase if you get buffer underruns.
-               [/help]
 [subcommand file]
        purpose = output plugin that writes to a local file
        [option filename]
diff --git a/mixer.c b/mixer.c
index ad674bf537e89c5d89bb0ebf946bfd14dad1a81a..efa42b93efb9ce0cf6f277b0f117806845455a26 100644 (file)
--- a/mixer.c
+++ b/mixer.c
@@ -1,6 +1,6 @@
 /* Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
 
-/** \file mixer.c A volume fader and alarm clock for OSS. */
+/** \file mixer.c A volume fader and alarm clock. */
 
 #include <regex.h>
 #include <lopsub.h>
@@ -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)))(mh);
 close_mixer:
        m->close(&h);
 free_sub_lpr:
index 42dd7539f647b1e1051870f673fc2f300cfa4a4d..471efd99db36634e1db33763be94ef70c4ae6de4 100644 (file)
--- 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 <id3tag.h>
@@ -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];
 }
 
index d4b765857a852013bd3145e1a042589957d3f2aa..311a514dc86ff6bfb01abf53d16a75672827af12 100644 (file)
@@ -57,7 +57,7 @@ static int get_oss_format(enum sample_format sf)
        case SF_S16_BE: return AFMT_S16_BE;
        case SF_U16_LE: return AFMT_U16_LE;
        case SF_U16_BE: return AFMT_U16_BE;
-       default: return AFMT_S16_LE;
+       default: return -E_BAD_SAMPLE_FORMAT;
        }
 }
 
@@ -113,10 +113,14 @@ static int oss_init(struct writer_node *wn, unsigned sample_rate,
        if (ret < 0)
                goto err;
        /* set PCM format */
-       sample_format = format = get_oss_format(sample_format);
+       ret = get_oss_format(sample_format);
+       if (ret < 0)
+               return ret;
+       sample_format = format = ret;
        ret = ioctl(powd->fd, SNDCTL_DSP_SETFMT, &format);
        if (ret < 0) {
                ret = -ERRNO_TO_PARA_ERROR(errno);
+               PARA_ERROR_LOG("could not set sample format\n");
                goto err;
        }
        ret = -E_BAD_SAMPLE_FORMAT;
index 5aa78b44bf79314dda28e82a83f0153f63d35e6d..7be817ddaa49bf97e5d4025c43be63b42da9cd4c 100644 (file)
@@ -48,6 +48,7 @@
 #include <speex/speex_callbacks.h>
 
 #include "para.h"
+#include "portable_io.h"
 #include "list.h"
 #include "sched.h"
 #include "buffer_tree.h"
@@ -121,12 +122,6 @@ static int speexdec_init(struct filter_node *fn)
        return 1;
 }
 
-#if !defined(__LITTLE_ENDIAN__) && ( defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) )
-#define le_short(s) ((short) ((unsigned short) (s) << 8) | ((unsigned short) (s) >> 8))
-#else
-#define le_short(s) ((short) (s))
-#endif
-
 /**
  * Size of the output buffer.
  *
@@ -178,7 +173,7 @@ static int speexdec_write_frames(int packet_no,
                samples = new_frame_size * psd->shi.channels;
                btr_output = para_malloc(2 * samples);
                for (i = 0; i < samples; i++)
-                       btr_output[i] = le_short(output[i + skip_idx]);
+                       btr_output[i] = read_u16(output + i + skip_idx);
                btr_add_output((char *)btr_output, samples * 2, btrn);
        }
        return 1;
index 742eafdfb1887a0dd9b9d34f15c6662d08dff828..65d2240891afc81f40e6b964d5c0f6b7b25a0ddc 100644 (file)
--- 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/vss.c b/vss.c
index 2cd0a1632922b0b2272aa183c0ddba8c4b4a3d30..73c7231128b94152268b1bc8ea02d1f524b25b6a 100644 (file)
--- a/vss.c
+++ b/vss.c
 #include "fd.h"
 
 extern struct misc_meta_data *mmd;
-
-extern void dccp_send_init(struct sender *);
-extern void http_send_init(struct sender *);
-extern void udp_send_init(struct sender *);
-
 extern const struct sender udp_sender, dccp_sender, http_sender;
 const struct sender * const senders[] = {
        &http_sender, &dccp_sender, &udp_sender, NULL};
@@ -946,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
 
index d91a2ad4d5626c137551ae31ba9bb13b2bfcc7b0..8395864a00a6e376634272954708cd96739afc2f 100644 (file)
@@ -17,7 +17,8 @@ systems. It is written in C and released under the GPLv2.
 </ul>
 
 <b> Author: </b> Andr&eacute; Noll,
-<a href="mailto:maan@tuebingen.mpg.de">maan@tuebingen.mpg.de</a>
+<a href="mailto:maan@tuebingen.mpg.de">maan@tuebingen.mpg.de</a>,
+Homepage: <a href="http://people.tuebingen.mpg.de/maan/">http://people.tuebingen.mpg.de/maan/</a>
 <br>
 Comments and bug reports are welcome. Please provide the version of
 paraslash you are using and relevant parts of the logs.
index e0f5ccf1866d06378d249b3177a923fb6d8aa502..2c806614d1e4ea001f990949d817dd5929e39a00 100644 (file)
@@ -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,