From: Andre Noll Date: Thu, 10 Jul 2014 17:53:06 +0000 (+0200) Subject: Merge branch 't/cpsi_improvement' X-Git-Tag: v0.5.3~13 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=ba3cef172b3f905315c0279d0a664df4d1db149c;hp=1ac3b91158524919e913e1e23c400c1591117a21 Merge branch 't/cpsi_improvement' Cooking since 2014-05-03. * t/cpsi_improvement: com_cpsi(): Tell the user if nothing was copied. --- diff --git a/NEWS b/NEWS index 508288ec..f6fca566 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,20 @@ NEWS ==== +------------------------------------------------- +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. + - Minor improvements to the bitstream API. + - The cpsi command now prints a meaningful error message if + none of the given patterns matched any audio file. + ---------------------------------------- 0.5.2 (2014-04-11) "orthogonal interior" ---------------------------------------- diff --git a/alsa_mix.c b/alsa_mix.c index be38e887..c860efc8 100644 --- a/alsa_mix.c +++ b/alsa_mix.c @@ -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; diff --git a/alsa_write.c b/alsa_write.c index ad59a829..3759306e 100644 --- a/alsa_write.c +++ b/alsa_write.c @@ -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) { diff --git a/ao_write.c b/ao_write.c index 730653c1..63d18afa 100644 --- a/ao_write.c +++ b/ao_write.c @@ -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(); } diff --git a/attribute.c b/attribute.c index ffe84102..7777a8dd 100644 --- a/attribute.c +++ b/attribute.c @@ -355,7 +355,7 @@ int com_mvatt(struct command_context *cc) struct remove_attribute_action_data { /** Message buffer. */ struct para_buffer pb; - /** Numver of attributes removed. */ + /** Number of attributes removed. */ int num_removed; /** Bitwise "or" of the removed attributes. */ uint64_t mask_of_removed_atts; @@ -438,7 +438,7 @@ int com_rmatt(struct command_context *cc) * used for unset attributes. * * In practice, not all 64 attributes are defined. In this case, the function - * only prints \a N + 1 charaters where \a N is the greatest id of a defined + * only prints \a N + 1 characters where \a N is the greatest id of a defined * attribute. */ void get_attribute_bitmap(const uint64_t *atts, char *buf) diff --git a/audioc.c b/audioc.c index 5f6b5ae1..fe7165ed 100644 --- a/audioc.c +++ b/audioc.c @@ -97,6 +97,15 @@ static void help_completer(struct i9e_completion_info *ci, result->matches = i9e_complete_commands(ci->word, audiod_completers); } +static void version_completer(struct i9e_completion_info *ci, + struct i9e_completion_result *cr) +{ + char *opts[] = {"-v", NULL}; + + if (ci->word_num <= 2 && ci->word && ci->word[0] == '-') + i9e_complete_option(opts, ci, cr); +} + static void stat_completer(struct i9e_completion_info *ci, struct i9e_completion_result *cr) { diff --git a/audiod.cmd b/audiod.cmd index ad8f67d0..18c802de 100644 --- a/audiod.cmd +++ b/audiod.cmd @@ -66,9 +66,14 @@ H: parser-friendly mode. N: tasks D: list current tasks U: tasks -H: print the list of task ids together with the status of each task +H: Print the list of task ids together with the status of each task. --- N: term D: terminate audiod U: term H: Stop all decoders, shut down connection to para_server and exit. +--- +N: version +D: print the version of para_audiod +U: version [-v] +H: If the -v option is given, a more detailed version text is printed. diff --git a/audiod_command.c b/audiod_command.c index 4485d9e9..56d922e6 100644 --- a/audiod_command.c +++ b/audiod_command.c @@ -29,6 +29,7 @@ #include "string.h" #include "write.h" #include "fd.h" +#include "version.h" #include "audiod_command_list.h" extern struct sched sched; @@ -419,6 +420,22 @@ static int com_cycle(int fd, int argc, char **argv) return 1; } +static int com_version(int fd, int argc, char **argv) +{ + int ret; + char *msg; + + if (argc > 1 && strcmp(argv[1], "-v") == 0) + msg = make_message("%s", version_text("audiod")); + else + msg = make_message("%s\n", version_single_line("audiod")); + ret = client_write(fd, msg); + free(msg); + if (ret >= 0) + close(fd); + return ret; +} + static int check_perms(uid_t uid) { int i; diff --git a/bitstream.c b/bitstream.c index ddad67bb..fb638d0a 100644 --- a/bitstream.c +++ b/bitstream.c @@ -11,9 +11,7 @@ * For licencing details see COPYING.LIB. */ -/** - * \file bitstream.c Bitstream API. - */ +/** \file bitstream.c Bitstream API for the wma decoder. */ #include #include @@ -58,8 +56,7 @@ static int build_table(struct vlc *vlc, int table_nb_bits, int nb_codes, const void *bits, const void *codes, int codes_size, uint32_t code_prefix, int n_prefix) { - int i, j, k, n, table_size, table_index, nb, n1, idx, code_prefix2, - symbol; + int i, j, k, n, table_size, table_index, nb, n1, idx; uint32_t code; VLC_TYPE(*table)[2]; @@ -69,62 +66,57 @@ static int build_table(struct vlc *vlc, int table_nb_bits, int nb_codes, table = &vlc->table[table_index]; for (i = 0; i < table_size; i++) { - table[i][1] = 0; //bits - table[i][0] = -1; //codes + table[i][1] = 0; /* bits */ + table[i][0] = -1; /* codes */ } - /* map codes and compute auxillary table sizes */ + /* map codes and compute auxiliary table sizes */ for (i = 0; i < nb_codes; i++) { GET_DATA(n, bits, i, 1); - GET_DATA(code, codes, i, codes_size); /* we accept tables with holes */ + n -= n_prefix; if (n <= 0) continue; - symbol = i; + GET_DATA(code, codes, i, codes_size); /* if code matches the prefix, it is in the table */ - n -= n_prefix; - code_prefix2 = code >> n; - if (n <= 0 || code_prefix2 != code_prefix) + if ((code >> n) != code_prefix) continue; if (n <= table_nb_bits) { /* no need to add another table */ j = (code << (table_nb_bits - n)) & (table_size - 1); nb = 1 << (table_nb_bits - n); for (k = 0; k < nb; k++) { - if (table[j][1] /* bits */ != 0) { - PARA_EMERG_LOG("incorrect code\n"); - exit(EXIT_FAILURE); - } - table[j][1] = n; //bits - table[j][0] = symbol; + assert(table[j][1] == 0); /* incorrect code */ + table[j][1] = n; /* bits */ + table[j][0] = i; j++; } } else { n -= table_nb_bits; j = (code >> n) & ((1 << table_nb_bits) - 1); /* compute table size */ - n1 = -table[j][1]; //bits + n1 = -table[j][1]; /* bits */ if (n > n1) n1 = n; - table[j][1] = -n1; //bits + table[j][1] = -n1; /* bits */ } } - /* fill auxillary tables recursively */ + /* fill auxiliary tables recursively */ for (i = 0; i < table_size; i++) { - n = table[i][1]; //bits + n = table[i][1]; /* bits */ if (n < 0) { n = -n; if (n > table_nb_bits) { n = table_nb_bits; - table[i][1] = -n; //bits + table[i][1] = -n; /* bits */ } idx = build_table(vlc, n, nb_codes, bits, codes, codes_size, (code_prefix << table_nb_bits) | i, n_prefix + table_nb_bits); /* vlc->table might have changed */ table = &vlc->table[table_index]; - table[i][0] = idx; //code + table[i][0] = idx; /* code */ } } return table_index; @@ -134,19 +126,15 @@ static int build_table(struct vlc *vlc, int table_nb_bits, int nb_codes, * Build VLC decoding tables suitable for use with get_vlc(). * * \param vlc The structure to be initialized. - * - * \param nb_bits Set the decoding table size (2^nb_bits) entries. The bigger - * it is, the faster is the decoding. But it should not be too big to save - * memory and L1 cache. '9' is a good compromise. - * - * \param nb_codes Number of vlcs codes. - * + * \param nb_bits Set the decoding table size (2^nb_bits) entries. + * \param nb_codes Number of vlc codes. * \param bits Table which gives the size (in bits) of each vlc code. - * * \param codes Table which gives the bit pattern of of each vlc code. - * * \param codes_size The number of bytes of each entry of the \a codes tables. * + * The bigger \a nb_bits is, the faster is the decoding. But it should not be + * too big to save memory and L1 cache. '9' is a good compromise. + * * The wrap and size parameters allow to use any memory configuration and * types (byte/word/long) to store the bits and codes tables. */ @@ -178,15 +166,14 @@ void free_vlc(struct vlc *vlc) * Parse a vlc code. * * \param gbc The getbit context structure. - * * \param table The vlc tables to use. - * - * \param bits The number of bits which will be read at once, must be - * identical to nb_bits in init_vlc(). - * - * \param max_depth The number of times bits bits must be read to completely + * \param bits The number of bits which will be read at once. + * \param max_depth The number of times \a bits bits must be read to completely * read the longest vlc code = (max_vlc_length + bits - 1) / bits. * + * The \a bits parameter must be identical to the \a nb_bits value supplied to + * \ref init_vlc(). + * * \return The vlc code. */ int get_vlc(struct getbit_context *gbc, VLC_TYPE(*table)[2], int bits, diff --git a/bitstream.h b/bitstream.h index a3393380..5890d08c 100644 --- a/bitstream.h +++ b/bitstream.h @@ -69,11 +69,13 @@ static inline unsigned int get_bit(struct getbit_context *gbc) /** * Initialize a getbit_context structure. * - * \param buffer The bitstream buffer. It must be 4 bytes larger then the - * actual read bits because the bitstream reader might read 32 bits at once and - * could read over the end. + * \param gbc The structure to initialize. + * \param buffer The bitstream buffer. + * \param size The size of the buffer in bytes. * - * \param bit_size The size of the buffer in bytes. + * The bitstream buffer must be 4 bytes larger then the actual read bits + * because the bitstream reader might read 32 bits at once and could read over + * the end. */ static inline void init_get_bits(struct getbit_context *gbc, const uint8_t *buffer, int size) @@ -88,4 +90,3 @@ void init_vlc(struct vlc *vlc, int nb_bits, int nb_codes, const void *bits, void free_vlc(struct vlc *vlc); int get_vlc(struct getbit_context *gbc, VLC_TYPE(*table)[2], int bits, int max_depth); - diff --git a/crypt_common.c b/crypt_common.c index 71aabf53..fb6e1ef4 100644 --- a/crypt_common.c +++ b/crypt_common.c @@ -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 301e2ca5..dc9385a7 100644 --- a/error.h +++ b/error.h @@ -20,7 +20,6 @@ DEFINE_ERRLIST_OBJECT_ENUM; #define GUI_THEME_ERRORS #define RINGBUFFER_ERRORS #define SCORE_ERRORS -#define RBTREE_ERRORS #define RECV_ERRORS #define IPC_ERRORS #define DCCP_SEND_ERRORS @@ -181,6 +180,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 \ diff --git a/m4/gengetopt/alsa_write.m4 b/m4/gengetopt/alsa_write.m4 index be56c984..b2c56218 100644 --- a/m4/gengetopt/alsa_write.m4 +++ b/m4/gengetopt/alsa_write.m4 @@ -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. +" + + diff --git a/t/makefile.test b/t/makefile.test index a2bd4e53..6ef9ac84 100644 --- a/t/makefile.test +++ b/t/makefile.test @@ -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 index 00000000..7a627aad --- /dev/null +++ b/t/t0005-man.sh @@ -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 diff --git a/t/test-lib.sh b/t/test-lib.sh index f1bb8cf6..0e702b53 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -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" diff --git a/web/manual.m4 b/web/manual.m4 index e809c8b2..1f1c7f2a 100644 --- a/web/manual.m4 +++ b/web/manual.m4 @@ -1245,8 +1245,9 @@ chunk table and reads the meta data. The audio format handler code is linked into para_server and executed via the _add_ command. The same code is also available as a stand-alone -tool, para_afh, which can be used to print the technical data, the -chunk table and the meta data of a file. +tool, para_afh, which prints the technical data, the chunk table +and the meta data of a file. Moreover, all audio format handlers are +combined in the afh receiver which is part of para_recv and para_play. ---------- Networking diff --git a/wma.h b/wma.h index f0ba7f63..15b9c5d4 100644 --- a/wma.h +++ b/wma.h @@ -20,8 +20,14 @@ struct asf_header_info { uint32_t bit_rate; /** Further decoding information (ignored). */ uint32_t flags1; - /** Whether to use exp_vlc, bit reservoir, variable block len. */ + /** Encodes exp_vlc, bit reservoir, vbl, number of block sizes. */ uint16_t flags2; + /** Whether exponents are coded with VLC codes. */ + bool use_exp_vlc; + /** If false, a frame is equal to a superframe. */ + bool use_bit_reservoir; + /** Whether blocks are of variable or of constant size. */ + bool use_variable_block_len; }; /* wma_common.c */ diff --git a/wma_afh.c b/wma_afh.c index c429020c..65795fd8 100644 --- a/wma_afh.c +++ b/wma_afh.c @@ -257,6 +257,15 @@ static int wma_get_file_info(char *map, size_t numbytes, __a_unused int fd, afhi->frequency = ahi.sample_rate; afhi->channels = ahi.channels; afhi->header_len = ahi.header_len; + + afhi->techinfo = make_message("%s%s%s%s%s", + ahi.use_exp_vlc? "exp vlc" : "", + (ahi.use_bit_reservoir && ahi.use_exp_vlc)? ", " : "", + ahi.use_bit_reservoir? "bit reservoir" : "", + (ahi.use_variable_block_len && + (ahi.use_exp_vlc || ahi.use_bit_reservoir)? ", " : ""), + ahi.use_variable_block_len? "vbl" : "" + ); wma_make_chunk_table(map + ahi.header_len, numbytes - ahi.header_len, ahi.block_align, afhi); read_asf_tags(map, ahi.header_len, &afhi->tags); diff --git a/wma_common.c b/wma_common.c index 97cdba0a..e53cdf5e 100644 --- a/wma_common.c +++ b/wma_common.c @@ -115,6 +115,9 @@ int read_asf_header(const char *buf, int loaded, struct asf_header_info *ahi) ahi->flags2 = read_u16(start + 60); PARA_INFO_LOG("read_asf_header: flags1: %d, flag2: %d\n", ahi->flags1, ahi->flags2); + ahi->use_exp_vlc = ahi->flags2 & 0x0001; + ahi->use_bit_reservoir = ahi->flags2 & 0x0002; + ahi->use_variable_block_len = ahi->flags2 & 0x0004; return 1; } diff --git a/wmadec_filter.c b/wmadec_filter.c index 0071337f..fdca2814 100644 --- a/wmadec_filter.c +++ b/wmadec_filter.c @@ -62,17 +62,11 @@ struct private_wmadec_data { /** Information contained in the audio file header. */ struct asf_header_info ahi; struct getbit_context gb; - /** Whether to use the bit reservoir. */ - int use_bit_reservoir; - /** Whether to use variable block length. */ - int use_variable_block_len; - /** Whether to use exponent coding. */ - int use_exp_vlc; /** Whether perceptual noise is added. */ int use_noise_coding; /** Depends on number of the bits per second and the frame length. */ int byte_offset_bits; - /** Only used if use_exp_vlc is true. */ + /** Only used if ahi->use_exp_vlc is true. */ struct vlc exp_vlc; uint16_t exponent_bands[BLOCK_NB_SIZES][25]; /** The index of the first coef in high band. */ @@ -96,7 +90,7 @@ struct private_wmadec_data { int frame_len; /** log2 of frame_len. */ int frame_len_bits; - /** Number of block sizes. */ + /** Number of block sizes, one if !ahi->use_variable_block_len. */ int nb_block_sizes; /* block info */ int reset_block_lengths; @@ -180,7 +174,7 @@ static void wmadec_cleanup(struct private_wmadec_data *pwd) for (i = 0; i < pwd->nb_block_sizes; i++) imdct_end(pwd->mdct_ctx[i]); - if (pwd->use_exp_vlc) + if (pwd->ahi.use_exp_vlc) free_vlc(&pwd->exp_vlc); if (pwd->use_noise_coding) free_vlc(&pwd->hgain_vlc); @@ -314,7 +308,7 @@ static int wma_init(struct private_wmadec_data *pwd) else pwd->frame_len_bits = 11; pwd->frame_len = 1 << pwd->frame_len_bits; - if (pwd->use_variable_block_len) { + if (pwd->ahi.use_variable_block_len) { int nb_max, nb; nb = ((flags2 >> 3) & 3) + 1; if ((ahi->bit_rate / ahi->channels) >= 32000) @@ -395,7 +389,7 @@ static int wma_init(struct private_wmadec_data *pwd) pwd->frame_len, bps, bps1, high_freq, pwd->byte_offset_bits); PARA_INFO_LOG("use_noise_coding=%d use_exp_vlc=%d nb_block_sizes=%d\n", - pwd->use_noise_coding, pwd->use_exp_vlc, pwd->nb_block_sizes); + pwd->use_noise_coding, pwd->ahi.use_exp_vlc, pwd->nb_block_sizes); compute_scale_factor_band_sizes(pwd, high_freq); /* init MDCT windows : simple sinus window */ @@ -410,7 +404,7 @@ static int wma_init(struct private_wmadec_data *pwd) if (pwd->use_noise_coding) { /* init the noise generator */ - if (pwd->use_exp_vlc) + if (pwd->ahi.use_exp_vlc) pwd->noise_mult = 0.02; else pwd->noise_mult = 0.04; @@ -484,10 +478,6 @@ static int wma_decode_init(char *initial_buf, int len, struct private_wmadec_dat return ret; } - pwd->use_exp_vlc = pwd->ahi.flags2 & 0x0001; - pwd->use_bit_reservoir = pwd->ahi.flags2 & 0x0002; - pwd->use_variable_block_len = pwd->ahi.flags2 & 0x0004; - ret = wma_init(pwd); if (ret < 0) return ret; @@ -504,7 +494,7 @@ static int wma_decode_init(char *initial_buf, int len, struct private_wmadec_dat wma_hgain_huffcodes, 2); } - if (pwd->use_exp_vlc) { + if (pwd->ahi.use_exp_vlc) { PARA_INFO_LOG("using exp_vlc\n"); init_vlc(&pwd->exp_vlc, EXPVLCBITS, sizeof(wma_scale_huffbits), wma_scale_huffbits, wma_scale_huffcodes, 4); @@ -850,7 +840,7 @@ static int wma_decode_block(struct private_wmadec_data *pwd) int nb_coefs[MAX_CHANNELS]; /* compute current block length */ - if (pwd->use_variable_block_len) { + if (pwd->ahi.use_variable_block_len) { n = wma_log2(pwd->nb_block_sizes - 1) + 1; if (pwd->reset_block_lengths) { @@ -927,7 +917,7 @@ static int wma_decode_block(struct private_wmadec_data *pwd) if ((pwd->block_len_bits == pwd->frame_len_bits) || get_bit(&pwd->gb)) { for (ch = 0; ch < pwd->ahi.channels; ch++) { if (pwd->channel_coded[ch]) { - if (pwd->use_exp_vlc) { + if (pwd->ahi.use_exp_vlc) { ret = decode_exp_vlc(pwd, ch); if (ret < 0) return ret; @@ -1096,7 +1086,7 @@ static int wma_decode_superframe(struct private_wmadec_data *pwd, void *data, buf_size = pwd->ahi.block_align; samples = data; init_get_bits(&pwd->gb, buf, buf_size); - if (pwd->use_bit_reservoir) { + if (pwd->ahi.use_bit_reservoir) { int i, nb_frames, bit_offset, pos, len; uint8_t *q;