Merge branch 't/audiod_com_version'
authorAndre Noll <maan@systemlinux.org>
Thu, 19 Jun 2014 13:39:33 +0000 (15:39 +0200)
committerAndre Noll <maan@systemlinux.org>
Thu, 19 Jun 2014 13:41:33 +0000 (15:41 +0200)
Cooking since 2014-04-03.

* t/audiod_com_version:
  audiod: Implement version command.
  audiod: Trival spelling fix for com_tasks().

24 files changed:
Makefile.real
NEWS
alsa_mix.c
alsa_write.c
ao_write.c
audiod.c
buffer_tree.c
client.c
client.h
client_common.c
configure.ac
crypt_common.c
error.h
flac_afh.c
gui.c
interactive.c
m4/gengetopt/alsa_write.m4
oggdec_filter.c
opusdec_filter.c
t/makefile.test
t/t0005-man.sh [new file with mode: 0755]
t/test-lib.sh
version.c
wma_afh.c

index d6ffe0c424a2adf2a623d8b2376f327932142911..2973637e517692bad60d1773e8feee5feb1c48f2 100644 (file)
@@ -86,7 +86,6 @@ DEBUG_CPPFLAGS += -Wredundant-decls
 DEBUG_CPPFLAGS += -Wall -Wno-sign-compare -Wno-unknown-pragmas
 DEBUG_CPPFLAGS += -Wformat-security
 DEBUG_CPPFLAGS += -Wmissing-format-attribute
-DEBUG_CPPFLAGS += -Wdeclaration-after-statement
 
 ifeq ($(uname_s),Linux)
        CPPFLAGS += -fdata-sections -ffunction-sections
diff --git a/NEWS b/NEWS
index ab53a17697ea7c92f2d0b2107d4d8dcee95d9e4c..1cc2f40ebdd980289de24e1e829c5662980d9f69 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,9 +1,20 @@
 NEWS
 ====
 
----------------------------------------------
-0.5.2 (to be announced) "orthogonal interior"
----------------------------------------------
+-------------------------------------------------
+0.5.3 (to be released) "symbolic synchronization"
+-------------------------------------------------
+
+       - Various alsa-related fixes, mostly for the raspberry pi.
+       - The test suite has been extended to include sanity checks
+         for the generated man pages.
+       - ao_writer fixes. This writer was in a quite bad shape. Many
+         serious bugs have been fixed.
+       - new audiod command: version.
+
+----------------------------------------
+0.5.2 (2014-04-11) "orthogonal interior"
+----------------------------------------
 
 The new sync filter, the AES_CTR128 stream cipher and the overhauled
 network code are the highlights of this release. It also includes a
@@ -23,9 +34,13 @@ fair number of smaller fixes and improvements not mentioned here.
        - The man pages of para_audiod, para_filter, para_recv, and
          para_write contain the relevant options for receivers, filters,
          writers. This broke in 0.5.0.
+       - ogg/vorbis latency improvements.
        - Improved user manual.
        - Minor fixes to avoid clang warnings.
 
+Downloads: ./releases/paraslash-0.5.2.tar.bz2 (tarball),
+./releases/paraslash-0.5.2.tar.bz2.asc (signature)
+
 ------------------------------------------
 0.5.1 (2013-12-20) "temporary implication"
 ------------------------------------------
index be38e887af9babf8925bcda4d8065a79f991ff7c..c860efc8aa34d9776f52375d87c4d9cb43ad3c27 100644 (file)
@@ -150,7 +150,7 @@ static int alsa_mix_set_channel(struct mixer_handle *h,
                        mixer_channel, h->card, snd_strerror(ret));
                return -E_ALSA_MIX_RANGE;
        }
-       if (h->pmin < 0 || h->pmax < 0 || h->pmin >= h->pmax) {
+       if (h->pmin >= h->pmax) {
                PARA_NOTICE_LOG("alsa reported %s range %ld-%ld (%s)\n",
                        mixer_channel, h->pmin, h->pmax, h->card);
                return -E_ALSA_MIX_RANGE;
index ad59a829e4d0967bad079b9df6caa0e976b8f77b..3759306ec2215f0732bba5fcc76d18d011db41e1 100644 (file)
@@ -34,17 +34,24 @@ struct private_alsa_write_data {
        snd_pcm_t *handle;
        /** Determined and set by alsa_init(). */
        int bytes_per_frame;
-       /**
-        * The sample rate given by command line option or the decoder
-        * of the writer node group.
+       /*
+        * If the sample rate is not given at the command line and no wav
+        * header was detected, the btr exec mechanism is employed to query the
+        * ancestor buffer tree nodes for this information. In a typical setup
+        * the decoder passes the sample rate back to the alsa writer.
+        *
+        *  \sa \ref btr_exec_up().
         */
        unsigned sample_rate;
-       snd_pcm_format_t sample_format;
-       /**
-        * The number of channels, given by command line option or the
-        * decoder of the writer node group.
+       /*
+        * The sample format (8/16 bit, signed/unsigned, little/big endian) is
+        * determined in the same way as the \a sample_rate.
         */
+       snd_pcm_format_t sample_format;
+       /* The number of channels, again determined like \a sample_rate. */
        unsigned channels;
+       /* time until buffer underrun occurs, in milliseconds */
+       unsigned buffer_time;
        struct timeval drain_barrier;
        /* File descriptor for select(). */
        int poll_fd;
@@ -72,9 +79,9 @@ static int alsa_init(struct private_alsa_write_data *pad,
        snd_pcm_uframes_t start_threshold, stop_threshold;
        snd_pcm_uframes_t buffer_size, period_size;
        snd_output_t *output_log;
-       unsigned buffer_time;
        int ret;
        const char *msg;
+       unsigned period_time;
 
        PARA_INFO_LOG("opening %s\n", conf->device_arg);
        msg = "unable to open pcm";
@@ -107,18 +114,21 @@ static int alsa_init(struct private_alsa_write_data *pad,
                &pad->sample_rate, NULL);
        if (ret < 0)
                goto fail;
-       msg = "unable to get buffer time";
-       ret = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time,
-               NULL);
-       if (ret < 0 || buffer_time == 0)
-               goto fail;
-       /* buffer at most 500 milliseconds */
-       buffer_time = PARA_MIN(buffer_time, 500U * 1000U);
+       /* alsa wants microseconds */
+       pad->buffer_time = conf->buffer_time_arg * 1000;
        msg = "could not set buffer time";
        ret = snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams,
-               &buffer_time, NULL);
+               &pad->buffer_time, NULL);
+       if (ret < 0)
+               goto fail;
+       pad->buffer_time /= 1000; /* we prefer milliseconds */
+       period_time = pad->buffer_time * 250; /* buffer time / 4 */
+       msg = "could not set period time";
+       ret = snd_pcm_hw_params_set_period_time_near(pad->handle, hwparams,
+               &period_time, 0);
        if (ret < 0)
                goto fail;
+
        msg = "unable to install hw params";
        ret = snd_pcm_hw_params(pad->handle, hwparams);
        if (ret < 0)
@@ -166,15 +176,16 @@ static int alsa_init(struct private_alsa_write_data *pad,
        if (ret == 0) {
                char *buf, *p;
                size_t sz;
-               PARA_INFO_LOG("dumping alsa configuration\n");
+               PARA_DEBUG_LOG("dumping alsa configuration\n");
                snd_pcm_dump(pad->handle, output_log);
+               snd_pcm_hw_params_dump(hwparams, output_log);
                sz = snd_output_buffer_string(output_log, &buf);
                for (p = buf; p < buf + sz;) {
                        char *q = memchr(p, '\n', buf + sz - p);
                        if (!q)
                                break;
                        *q = '\0';
-                       PARA_INFO_LOG("%s\n", p);
+                       PARA_DEBUG_LOG("%s\n", p);
                        p = q + 1;
                }
                snd_output_close(output_log);
@@ -207,11 +218,12 @@ static void alsa_write_pre_select(struct sched *s, struct task *t)
                sched_request_barrier_or_min_delay(&pad->drain_barrier, s);
                return;
        }
+       /* wait at most 50% of the buffer time */
+       sched_request_timeout_ms(pad->buffer_time / 2, s);
        ret = snd_pcm_poll_descriptors(pad->handle, &pfd, 1);
        if (ret < 0) {
                PARA_ERROR_LOG("could not get alsa poll fd: %s\n",
                        snd_strerror(-ret));
-               t->error = -E_ALSA;
                return;
        }
        pad->poll_fd = pfd.fd;
@@ -296,21 +308,13 @@ again:
                wn->min_iqs = pad->bytes_per_frame;
                goto again;
        }
-       if (pad->poll_fd < 0 || !FD_ISSET(pad->poll_fd, &s->rfds))
-               return 0;
        frames = bytes / pad->bytes_per_frame;
        frames = snd_pcm_writei(pad->handle, data, frames);
        if (frames == 0 || frames == -EAGAIN) {
-               /*
-                * The alsa poll fd was ready for IO but we failed to write to
-                * the alsa handle. This means another event is pending. We
-                * don't care about that but we have to dispatch the event in
-                * order to avoid a busy loop. So we simply read from the poll
-                * fd.
-                */
                char buf[100];
-               if (read(pad->poll_fd, buf, 100))
-                       do_nothing;
+               if (pad->poll_fd >= 0 && FD_ISSET(pad->poll_fd, &s->rfds))
+                       if (read(pad->poll_fd, buf, 100))
+                               do_nothing;
                return 0;
        }
        if (frames > 0) {
index 730653c1b06844ed0822cc784c0c61b6a0303269..63d18afab387288831e45e50cd33ecfd42801805 100644 (file)
@@ -28,6 +28,7 @@ struct private_aow_data {
 
        pthread_t thread;
        pthread_attr_t attr;
+       /* The mutex and the condition variable serialize access to ->btrn */
        pthread_mutex_t mutex;
        pthread_cond_t data_available;
        struct btr_node *thread_btrn;
@@ -39,19 +40,46 @@ static void aow_close(struct writer_node *wn)
 
        if (!pawd)
                return;
+       assert(!pawd->thread_btrn);
        ao_close(pawd->dev);
        free(pawd);
        wn->private_data = NULL;
-       ao_shutdown();
 }
 
 static void aow_pre_select(struct sched *s, struct task *t)
 {
        struct writer_node *wn = container_of(t, struct writer_node, task);
-       int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+       struct private_aow_data *pawd = wn->private_data;
+       int ret;
 
-       if (ret == 0)
-               return;
+       if (!pawd) { /* not yet started */
+               assert(wn->btrn);
+               ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+               if (ret != 0)
+                       goto min_delay;
+               return; /* no data available */
+       }
+       if (!wn->btrn) { /* EOF */
+               if (!pawd->thread_btrn) /* ready to exit */
+                       goto min_delay;
+               /* wait for the play thread to terminate */
+               goto timeout;
+       }
+       pthread_mutex_lock(&pawd->mutex);
+       ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+       pthread_mutex_unlock(&pawd->mutex);
+       if (ret != 0)
+               goto min_delay;
+       /*
+        * Even though the node status is zero, we might have data available,
+        * but the output buffer is full. If we don't set a timeout here, we
+        * are woken up only if new data arrives, which might be too late and
+        * result in a buffer underrun in the playing thread. To avoid this we
+        * never sleep longer than the (default) buffer time.
+        */
+timeout:
+       return sched_request_timeout_ms(20, s);
+min_delay:
        sched_min_delay(s);
 }
 
@@ -129,7 +157,6 @@ static int aow_init(struct writer_node *wn, unsigned sample_rate,
        struct private_aow_data *pawd = para_malloc(sizeof(*pawd));
        struct ao_write_args_info *conf = wn->conf;
 
-       ao_initialize();
        if (conf->driver_given) {
                ret = -E_AO_BAD_DRIVER;
                id = ao_driver_id(conf->driver_arg);
@@ -192,16 +219,12 @@ __noreturn static void *aow_play(void *priv)
        char *data;
        int ret;
 
+       pthread_mutex_lock(&pawd->mutex);
        for (;;) {
-               /*
-                * Lock mutex and wait for signal. pthread_cond_wait() will
-                * automatically and atomically unlock mutex while it waits.
-                */
-               pthread_mutex_lock(&pawd->mutex);
                for (;;) {
                        ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
                        if (ret < 0)
-                               goto unlock;
+                               goto fail;
                        if (ret > 0) {
                                btr_merge(btrn, wn->min_iqs);
                                bytes = btr_next_buffer(btrn, &data);
@@ -210,26 +233,35 @@ __noreturn static void *aow_play(void *priv)
                                        break;
                                /* eof and less than a single frame available */
                                ret = -E_WRITE_COMMON_EOF;
-                               goto unlock;
+                               goto fail;
                        }
-                       //PARA_CRIT_LOG("waiting for data\n");
-                       //usleep(1000);
-                       //pthread_mutex_unlock(&pawd->mutex);
-                       pthread_cond_wait(&pawd->data_available, &pawd->mutex);
+                       /*
+                        * No data available, go to sleep and wait for the main
+                        * thread to wake us up. pthread_cond_wait() unlocks
+                        * the mutex while it waits and locks it again upon
+                        * return.
+                        */
+                       ret = pthread_cond_wait(&pawd->data_available,
+                               &pawd->mutex);
+                       /* pthread_cond_wait() can never fail here */
+                       assert(ret == 0);
                }
-               pthread_mutex_unlock(&pawd->mutex);
                assert(frames > 0);
                bytes = frames * pawd->bytes_per_frame;
-               ret = -E_AO_PLAY;
-               if (ao_play(pawd->dev, data, bytes) == 0) /* failure */
-                       goto out;
+               pthread_mutex_unlock(&pawd->mutex);
+               ret = ao_play(pawd->dev, data, bytes);
+               pthread_mutex_lock(&pawd->mutex);
+               if (ret == 0) { /* failure */
+                       ret = -E_AO_PLAY;
+                       goto fail;
+               }
                btr_consume(btrn, bytes);
        }
-unlock:
-       pthread_mutex_unlock(&pawd->mutex);
-out:
+fail:
+       btr_remove_node(&pawd->thread_btrn);
        assert(ret < 0);
        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
+       pthread_mutex_unlock(&pawd->mutex);
        pthread_exit(NULL);
 }
 
@@ -314,21 +346,32 @@ static int aow_post_select(__a_unused struct sched *s,
                        goto remove_thread_btrn;
                return 0;
        }
+       if (!wn->btrn) {
+               if (!pawd->thread_btrn) {
+                       pthread_join(pawd->thread, NULL);
+                       return -E_AO_EOF;
+               }
+               PARA_INFO_LOG("waiting for play thread to terminate\n");
+               return 0;
+       }
        pthread_mutex_lock(&pawd->mutex);
        ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
        if (ret > 0) {
                btr_pushdown(wn->btrn);
-               pthread_cond_signal(&pawd->data_available);
+               if (pthread_cond_signal(&pawd->data_available) != 0) {
+                       ret = -E_AO_PTHREAD;
+                       PARA_ERROR_LOG("pthread_cond_signal() failed\n");
+                       goto remove_thread_btrn;
+               }
        }
-       pthread_mutex_unlock(&pawd->mutex);
-       if (ret >= 0)
+       if (ret >= 0) {
+               pthread_mutex_unlock(&pawd->mutex);
                goto out;
-       pthread_mutex_lock(&pawd->mutex);
+       }
        btr_remove_node(&wn->btrn);
-       PARA_INFO_LOG("waiting for thread to terminate\n");
        pthread_cond_signal(&pawd->data_available);
        pthread_mutex_unlock(&pawd->mutex);
-       pthread_join(pawd->thread, NULL);
+       return 0;
 remove_thread_btrn:
        btr_remove_node(&pawd->thread_btrn);
 remove_btrn:
@@ -410,6 +453,5 @@ void ao_write_init(struct writer *w)
        dh[num_lines] = NULL;
        w->help.detailed_help = (const char **)dh;
        ao_write_cmdline_parser_free(&dummy);
-       ao_shutdown();
 }
 
index d815c4aa6c98e699dc48304cd96db1d14c28035b..a8f401834b8a438d7b21d96920bc7b735f0a6c6b 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -1153,13 +1153,10 @@ static void close_unused_slots(void)
  */
 void __noreturn clean_exit(int status, const char *msg)
 {
-       int i;
-
        if (socket_name)
                unlink(socket_name);
        close_stat_pipe();
-       FOR_EACH_SLOT(i)
-               close_slot(i);
+       close_unused_slots();
        audiod_cmdline_parser_free(&conf);
        close_stat_clients();
        PARA_EMERG_LOG("%s\n", msg);
index 5afa3ad3deda281415cfd172af0c13092b95ad67..44b73c946d1fc6127aba2b1e9e31c206b67ba18c 100644 (file)
@@ -771,6 +771,12 @@ void btr_drain(struct btr_node *btrn)
                btr_drop_buffer_reference(br);
 }
 
+static void btr_free_node(struct btr_node *btrn)
+{
+       free(btrn->name);
+       free(btrn);
+}
+
 /**
  * Remove a node from a buffer tree.
  *
@@ -798,8 +804,7 @@ void btr_remove_node(struct btr_node **btrnp)
        btr_drain(btrn);
        if (btrn->parent)
                list_del(&btrn->node);
-       free(btrn->name);
-       free(btrn);
+       btr_free_node(btrn);
 out:
        *btrnp = NULL;
 }
@@ -862,6 +867,7 @@ void btr_splice_out_node(struct btr_node **btrnp)
                        list_del(&ch->node);
        }
        assert(list_empty(&btrn->children));
+       btr_free_node(btrn);
        *btrnp = NULL;
 }
 
index c1ef5217345eb96720e7ce81e495e681f6d774f2..b39a8b0199f0fbe66f95c12a34c5c6b05b5d23d7 100644 (file)
--- a/client.c
+++ b/client.c
@@ -114,7 +114,6 @@ static int execute_client_command(const char *cmd, char **result)
        schedule(&command_sched);
        *result = exec_task.result_buf;
        btr_remove_node(&exec_task.btrn);
-       client_disconnect(ct);
        ret = 1;
 out:
        btr_remove_node(&exec_task.btrn);
@@ -444,7 +443,6 @@ static int client_i9e_line_handler(char *line)
 {
        int ret;
 
-       client_disconnect(ct);
        PARA_DEBUG_LOG("line: %s\n", line);
        ret = make_client_argv(line);
        if (ret <= 0)
index 82dbc03327260cb9d3810c47f0e8d972f421d70e..e304f0923693b5913a5224c4be8327a0fa6795f1 100644 (file)
--- a/client.h
+++ b/client.h
@@ -52,7 +52,6 @@ struct client_task {
        char **features;
 };
 
-void client_disconnect(struct client_task *ct);
 void client_close(struct client_task *ct);
 int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
                int *loglevel);
index 900d3653893dea7067330d60a929ce0a4b13d902..8212abb1d611dcff88889fa70629fcdc9f6eb5cf 100644 (file)
 /** The size of the receiving buffer. */
 #define CLIENT_BUFSIZE 4000
 
-/**
- * Close the connection to para_server and deallocate per-command resources.
- *
- * \param ct The client task.
- *
- * This frees all resources of the current command but keeps the configuration
- * in \p ct->conf.
- *
- * \sa \ref client_close().
- */
-void client_disconnect(struct client_task *ct)
-{
-       if (!ct)
-               return;
-       if (ct->scc.fd >= 0)
-               close(ct->scc.fd);
-       free_argv(ct->features);
-       ct->features = NULL;
-       sc_free(ct->scc.recv);
-       ct->scc.recv = NULL;
-       sc_free(ct->scc.send);
-       ct->scc.send = NULL;
-       btr_remove_node(&ct->btrn[0]);
-       btr_remove_node(&ct->btrn[1]);
-}
-
 /**
  * Close the connection to para_server and free all resources.
  *
  * \param ct Pointer to the client data.
  *
- * \sa \ref client_open(), \ref client_disconnect().
+ * \sa \ref client_open().
  */
 void client_close(struct client_task *ct)
 {
        if (!ct)
                return;
-       client_disconnect(ct);
        free(ct->user);
        free(ct->config_file);
        free(ct->key_file);
@@ -476,6 +449,16 @@ out:
        btr_remove_node(&ct->btrn[1]);
        if (ret != -E_SERVER_CMD_SUCCESS && ret != -E_SERVER_CMD_FAILURE)
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       if (ct->scc.fd >= 0) {
+               close(ct->scc.fd);
+               ct->scc.fd = -1;
+       }
+       free_argv(ct->features);
+       ct->features = NULL;
+       sc_free(ct->scc.recv);
+       ct->scc.recv = NULL;
+       sc_free(ct->scc.send);
+       ct->scc.send = NULL;
        return ret;
 }
 
index 07ecffdd71f31e1246d8262bda5fbdd762212f99..30bce8ee400858cb099c3bc7d1d2593c4f842225 100644 (file)
@@ -757,7 +757,15 @@ if test "$have_readline" = "yes"; then
 fi
 
 if test "$have_readline" = "yes"; then
-       :
+       AC_CHECK_DECL(
+               [rl_free_keymap],
+               [AC_DEFINE(RL_FREE_KEYMAP_DECLARED, 1, readline >= 6.3)],
+               [],
+               [
+                       #include <stdio.h>
+                       #include <readline/readline.h>
+               ]
+       )
        AC_SUBST(readline_cppflags)
        AC_SUBST(readline_ldflags)
        AC_DEFINE(HAVE_READLINE, 1, define to 1 to turn on readline support)
index 71aabf53f944eea64d4214fccff354787b7e6f9b..fb6e1ef4174931a9201ea571b7ea33ecd09bf54c 100644 (file)
@@ -90,41 +90,31 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize)
 
                switch (state) {
                case 0:
-                       if (target) {
-                               if (tarindex >= targsize)
-                                       return -E_BASE64;
-                               target[tarindex] = (pos - Base64) << 2;
-                       }
+                       if (tarindex >= targsize)
+                               return -E_BASE64;
+                       target[tarindex] = (pos - Base64) << 2;
                        state = 1;
                        break;
                case 1:
-                       if (target) {
-                               if (tarindex + 1 >= targsize)
-                                       return -E_BASE64;
-                               target[tarindex]   |=  (pos - Base64) >> 4;
-                               target[tarindex+1]  = ((pos - Base64) & 0x0f)
-                                                       << 4 ;
-                       }
+                       if (tarindex + 1 >= targsize)
+                               return -E_BASE64;
+                       target[tarindex] |= (pos - Base64) >> 4;
+                       target[tarindex + 1] = ((pos - Base64) & 0x0f) << 4;
                        tarindex++;
                        state = 2;
                        break;
                case 2:
-                       if (target) {
-                               if (tarindex + 1 >= targsize)
-                                       return -E_BASE64;
-                               target[tarindex]   |=  (pos - Base64) >> 2;
-                               target[tarindex+1]  = ((pos - Base64) & 0x03)
-                                                       << 6;
-                       }
+                       if (tarindex + 1 >= targsize)
+                               return -E_BASE64;
+                       target[tarindex] |= (pos - Base64) >> 2;
+                       target[tarindex + 1] = ((pos - Base64) & 0x03) << 6;
                        tarindex++;
                        state = 3;
                        break;
                case 3:
-                       if (target) {
-                               if (tarindex >= targsize)
-                                       return -E_BASE64;
-                               target[tarindex] |= (pos - Base64);
-                       }
+                       if (tarindex >= targsize)
+                               return -E_BASE64;
+                       target[tarindex] |= pos - Base64;
                        tarindex++;
                        state = 0;
                        break;
@@ -170,7 +160,7 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize)
                         * zeros.  If we don't check them, they become a
                         * subliminal channel.
                         */
-                       if (target && target[tarindex] != 0)
+                       if (target[tarindex] != 0)
                                return -E_BASE64;
                }
        } else {
diff --git a/error.h b/error.h
index 301e2ca5e1ae36a045a044d8f69c072e82c62a88..ba8409dfff4e42375629ba50f8a99281ac1aa574 100644 (file)
--- a/error.h
+++ b/error.h
@@ -181,6 +181,7 @@ extern const char **para_errlist[];
        PARA_ERROR(AO_PLAY, "ao_play() failed"), \
        PARA_ERROR(AO_BAD_SAMPLE_FORMAT, "ao: unsigned sample formats not supported"), \
        PARA_ERROR(AO_PTHREAD, "pthread error"), \
+       PARA_ERROR(AO_EOF, "ao: end of file"), \
 
 
 #define COMPRESS_FILTER_ERRORS \
index 21f86f4645bfd483e4a23f3e5c577a8626e7b630..eeb0e86c32ac558cc483fe1fc4ce1270c8f0d2f8 100644 (file)
@@ -80,7 +80,7 @@ static FLAC__int64 meta_tell_cb(FLAC__IOHandle handle)
 static int meta_eof_cb(FLAC__IOHandle handle)
 {
        struct private_flac_afh_data *pfad = handle;
-       return pfad->fpos == pfad->map_bytes - 1;
+       return pfad->fpos == pfad->map_bytes;
 }
 
 static int meta_close_cb(FLAC__IOHandle __a_unused handle)
diff --git a/gui.c b/gui.c
index 0f0a4a94d435b6ca836d99b95cb76a74ecbfc6f8..5a2e659d159e6b6225b5ec94adc2796138d5b358 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -560,7 +560,6 @@ static void setup_signal_handling(void)
        para_install_sighandler(SIGCHLD);
        para_install_sighandler(SIGWINCH);
        para_install_sighandler(SIGUSR1);
-       para_sigaction(SIGHUP, SIG_IGN);
 }
 
 /* kill every process in the process group and exit */
index ee953bf12a26377bb02efb3fc96e1b3cf3e7b0e9..9f2b71953c90f3148d17af0becf0b6a9f3eeaecd 100644 (file)
@@ -227,6 +227,7 @@ static void wipe_bottom_line(void)
        fprintf(i9ep->stderr_stream, "\r");
 }
 
+#ifndef RL_FREE_KEYMAP_DECLARED
 /**
  * Free all storage associated with a keymap.
  *
@@ -237,6 +238,7 @@ static void wipe_bottom_line(void)
  * \param keymap The keymap to deallocate.
  */
 void rl_free_keymap(Keymap keymap);
+#endif
 
 /**
  * Reset the terminal and save the in-memory command line history.
index be56c9849144b9a266aca80bf6bb2b5a31c15a40..b2c56218dcd6f6a2e41646fafebb883c74b09089 100644 (file)
@@ -8,11 +8,29 @@ include(header.m4)
 option "device" d
 #~~~~~~~~~~~~~~~~
 "set PCM device"
-string typestr="device"
-default="default"
+string typestr = "device"
+default = "default"
 optional
-details="
-       On systems with dmix, a better choice than the default
-       value might be to use \"plug:swmix\".
+details = "
+       Check for the presence of a /proc/asound/ directory to see if
+       ALSA is present in your kernel. The file /proc/asound/devices
+       contains all devices ALSA knows about.
 "
+
+option "buffer-time" B
+#~~~~~~~~~~~~~~~~~~~~~
+"duration of the ALSA buffer"
+int typestr = "milliseconds"
+default = "170"
+optional
+details = "
+       This is only a hint as ALSA might pick a slightly different
+       time, depending on the sound hardware. The chosen value is
+       shown in debug output as BUFFER_TIME.
+
+       If synchronization between multiple clients is desired,
+       the same buffer time should be configured for all clients.
+"
+
 </qu>
+
index 1eaa79300f5f70a9310ca17d7c52e5ba38e76a5b..3222b4aaaaabc046ac3983359ae58d868861c46a 100644 (file)
@@ -136,14 +136,13 @@ open:
                0, /* no initial bytes */
                ovc); /* the ov_open_callbacks */
        if (oret == OV_ENOTVORBIS || oret == OV_EBADHEADER) {
-               /* this might be due to the input buffer being too small */
+               /* maybe the input buffer is too small */
                if (!btr_no_parent(btrn)) {
                        fn->min_iqs += 1000;
                        iqs = btr_get_input_queue_size(btrn);
                        ret = 0;
                        if (iqs < fn->min_iqs)
                                goto out;
-                       PARA_CRIT_LOG("iqs: %zu\n", iqs);
                        btr_merge(btrn, fn->min_iqs);
                        pod->converted = 0;
                        goto open;
@@ -236,13 +235,8 @@ static int ogg_post_select(__a_unused struct sched *s, struct task *t)
                        break;
                fn->min_iqs = 0;
                have += ret;
-               if (have < OGGDEC_OUTPUT_CHUNK_SIZE)
-                       continue;
-               if (btr_get_output_queue_size(btrn) > OGGDEC_MAX_OUTPUT_SIZE)
+               if (have >= OGGDEC_OUTPUT_CHUNK_SIZE)
                        break;
-               btr_add_output(buf, have, btrn);
-               buf = para_malloc(OGGDEC_OUTPUT_CHUNK_SIZE);
-               have = 0;
        }
        pod->have_more = (ret > 0);
        if (have > 0) {
index d7489859556d111e12fc20c43785c517c033b144..9022fbab63e0c3e2626528bcfa752ff6fde8faa3 100644 (file)
@@ -277,7 +277,7 @@ static void opusdec_pre_select(struct sched *s, struct task *t)
 
        if (ret != 0)
                return sched_min_delay(s);
-       if (ctx->have_more)
+       if (!ctx->have_more)
                return;
        if (btr_get_output_queue_size(fn->btrn) <= OPUSDEC_MAX_OUTPUT_SIZE)
                return sched_min_delay(s);
index a2bd4e538a473e6caea8342e6fdb2e102ccc6aac..6ef9ac84862be90980474f699911421140fc6618 100644 (file)
@@ -8,6 +8,7 @@ test_options += --results-dir $(results_dir)
 test_options += --trash-dir $(trash_dir)
 test_options += --executables "$(prefixed_executables)"
 test_options += --objects "$(basename $(all_objs))"
+test_options += --man-dir $(man_dir)
 
 ifdef V
        ifeq ("$(origin V)", "command line")
diff --git a/t/t0005-man.sh b/t/t0005-man.sh
new file mode 100755 (executable)
index 0000000..7a627aa
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/bin/env bash
+
+test_description='Parse generated man pages.
+
+Simple sanity checks of some man pages.
+
+* para_audiod: Check that list of audiod commands is present
+* para_server: Check that list of server/afs commands is present
+* para_play: Check that list of play commands is present
+
+* para_{recv,filter,write,audiod}: Check for presence of
+filter/receiver/writer options as appropriate '
+
+. ${0%/*}/test-lib.sh
+
+rfw_regex='Options for .\{100,\}Options for ' # recv/filter/writer
+
+grep_man()
+{
+       local regex="$1" exe="$2"
+       tr '\n' ' ' < "$o_man_dir/para_$exe.1" | grep -q "$regex"
+}
+
+# check that options of all reveivers/filters/writers are contained
+# in the man pages
+
+regex="$rfw_regex"
+test_expect_success 'para_recv: receiver options' "grep_man '$regex' recv"
+test_expect_success 'para_filter: filter options' "grep_man '$regex' filter"
+test_expect_success 'para_write: writer options' "grep_man '$regex' write"
+test_require_objects "audiod"
+if [[ -n "$result" ]]; then
+       test_skip 'para_audiod' "missing object(s): $result"
+else
+       test_expect_success 'para_audiod: recv/filter/writer options' \
+               "grep_man '$regex' audiod"
+fi
+
+# check various command lists
+
+test_require_objects "audiod"
+if [[ -n "$result" ]]; then
+       test_skip 'para_audiod' "missing object(s): $result"
+else
+       regex='LIST OF AUDIOD COMMANDS.\{200,\}'
+       test_expect_success 'para_audiod: command list' \
+               "grep_man '$regex' audiod"
+fi
+
+test_require_objects "server"
+missing_objects="$result"
+if [[ -n "$missing_objects" ]]; then
+       test_skip "para_server" "missing object(s): $missing_objects"
+else
+       regex='LIST OF SERVER COMMANDS.\{100,\}LIST OF AFS COMMANDS'
+       test_expect_success 'para_server: server/afs commands' \
+               "grep_man '$regex' server"
+fi
+
+# para_play is always built
+regex='LIST OF COMMANDS.\{100,\}'
+test_expect_success 'para_play: play commands' "grep_man '$regex' play"
+regex="$rfw_regex"
+test_done
index f1bb8cf65b66ff3890659f2b6009bae08d3cbd77..0e702b53c9b4c4150418dc0e2a07e55949817b04 100644 (file)
@@ -21,22 +21,22 @@ say_color()
        if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
                export TERM=$ORIGINAL_TERM
                case "$1" in
-                       error) tput bold; tput setaf 1;;
-                       skip)  tput setaf 5;;
+                       error) tput $C_BOLD; tput $C_SETAF 1;;
+                       skip)  tput $C_SETAF 5;;
                        ok)
                                (($o_verbose == 0)) && return
-                               tput setaf 2;;
-                       pass)  tput bold; tput setaf 2;;
-                       info)  tput setaf 3;;
+                               tput $C_SETAF 2;;
+                       pass)  tput $C_BOLD; tput $C_SETAF 2;;
+                       info)  tput $C_SETAF 3;;
                        run)
                                (($o_verbose == 0)) && return
-                               tput setaf 6;;
+                               tput $C_SETAF 6;;
                esac
        fi
        shift
        printf "%s\n" "$*"
        if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
-               tput sgr0
+               tput $C_SGR0
                export TERM=dumb
        fi
 }
@@ -218,9 +218,21 @@ can_use_colors()
        result="false"
        [[ "$TERM" == "dumb" ]] && return
        [[ -t 1 ]] || return
-       tput bold >/dev/null 2>&1 || return
-       tput setaf 1 >/dev/null 2>&1 || return
-       tput sgr0 >/dev/null 2>&1 || return
+       C_BOLD='bold'
+       tput $C_BOLD &>/dev/null || {
+               C_BOLD='md'
+               tput $C_BOLD &>/dev/null
+       } || return
+       C_SETAF='setaf'
+       tput $C_SETAF 1 &>/dev/null || {
+               C_SETAF='AF'
+               tput $C_SETAF 1 &>/dev/null
+       } || return
+       C_SGR0='sgr0'
+       tput $C_SGR0 >/dev/null 2>&1 || {
+               C_SGR0='me'
+               tput $C_SGR0 &>/dev/null
+       } || return
        result="true"
 }
 
@@ -235,6 +247,7 @@ parse_options()
                -v=1|--verbose=1) o_verbose="1"; shift;;
                -v|--verbose|-v=2|--verbose=2) o_verbose="2"; shift;;
                --no-color) o_nocolor="true"; shift;;
+               --man-dir) export o_man_dir="$2"; shift; shift;;
                --results-dir) o_results_dir="$2"; shift; shift;;
                --trash-dir) o_trash_dir="$2"; shift; shift;;
                --executables-dir) export o_executables_dir="$2"; shift; shift;;
@@ -265,11 +278,13 @@ fixup_dirs()
        [[ -z "$o_results_dir" ]] && o_results_dir="$test_dir/test-results"
        [[ -z "$o_executables_dir" ]] && o_executables_dir="$test_dir/.."
        [[ -z "$o_trash_dir" ]] && o_trash_dir="$test_dir/trashes"
+       [[ -z "$o_man_dir" ]] && o_man_dir="$test_dir/../build/man/man1"
 
        # we want alsolute paths because relative paths become invalid
        # after changing to the trash dir
        [[ -n "${o_results_dir##/*}" ]] && o_results_dir="$wd/$o_results_dir"
        [[ -n "${o_executables_dir##/*}" ]] && o_executables_dir="$wd/$o_results_dir"
+       [[ -n "${o_man_dir##/*}" ]] && o_man_dir="$wd/$o_man_dir"
        [[ -n "${o_trash_dir##/*}" ]] && o_trash_dir="$wd/$o_trash_dir"
 
        mkdir -p "$o_results_dir"
index 8526bbcc47b29d6483c13a7ed0b6885a8e6fb061..3a190e634a6a4445edb200ecb34b205dbf762def 100644 (file)
--- a/version.c
+++ b/version.c
@@ -57,7 +57,7 @@ const char *version_text(const char *pfx)
        static char buf[512];
 
        snprintf(buf, sizeof(buf) - 1, "%s\n"
-               "Copyright (C) 2013-2014 Andre Noll\n"
+               "Copyright (C) 2014 Andre Noll\n"
                "This is free software with ABSOLUTELY NO WARRANTY."
                " See COPYING for details.\n"
                "Report bugs to <maan@systemlinux.org>.\n"
index 7ac1ba64354ab87bbbbbd8494e6d7f171f3c30e3..c429020c4e3455aa56929d5a36e60df315de7e28 100644 (file)
--- a/wma_afh.c
+++ b/wma_afh.c
@@ -132,7 +132,7 @@ static const char album_tag_header[] = { /* WM/AlbumTitle */
 static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti)
 {
        const char *p, *end = buf + buf_size, *q;
-       uint16_t len1, len2, len3, len4, len5;
+       uint16_t len1, len2, len3, len4;
 
        p = search_pattern(comment_header, sizeof(comment_header),
                buf, buf_size);
@@ -149,7 +149,7 @@ static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti)
        p += 2;
        len4 = read_u16(p);
        p += 2;
-       len5 = read_u16(p);
+       /* ignore length of the rating information */
        p += 2;
        if (p + len1 >= end)
                goto next;
@@ -158,10 +158,10 @@ static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti)
        if (p + len2 >= end)
                goto next;
        ti->artist = get_str16(p, len2);
-       p += len2 + len3 + len4;
-       if (p + len5 >= end)
+       p += len2 + len3;
+       if (p + len4 >= end)
                goto next;
-       ti->comment = get_str16(p, len5);
+       ti->comment = get_str16(p, len4);
 next:
        p = search_pattern(extended_content_header, sizeof(extended_content_header),
                buf, buf_size);