Simon Morlat <simon.morlat@linphone.org> (ortp)
-Christof Müller (bug reports)
+Christof Müller (bug reports)
M. Hari Nezumi <magenta@trikuare.cx> (AudioCompress)
Guillaume Outters <guillaume.outters@free.fr> (mosx-mpg123)
-Christian Reißmann (design)
+Christian Reißmann (design)
Gerrit Renker <gerrit@erg.abdn.ac.uk> (dccp improvements, IPv6 support)
HAVE_OGGVORBIS \
__GNUC__=4 \
__GNUC_MINOR__=4 \
- HAVE_UCRED
+ HAVE_UCRED \
+ HAVE_CLOCK_GETTIME
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
PACKAGE_STRING := @PACKAGE_STRING@
install_sh := @install_sh@
executables := @executables@
+ggo_descriptions_declared := @ggo_descriptions_declared@
GENGETOPT := @gengetopt@
HELP2MAN := @help2man@
uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS")
uname_rs := $(shell uname -rs)
cc_version := $(shell $(CC) --version | head -n 1)
-codename := spectral gravity
GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
CPPFLAGS += -DBINDIR='"$(BINDIR)"'
CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
-CPPFLAGS += -DCODENAME='"$(codename)"'
CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
CPPFLAGS += -Werror-implicit-function-declaration
CPPFLAGS += -Wmissing-noreturn
CPPFLAGS += -I$(cmdline_dir)
CPPFLAGS += @osl_cppflags@
+LDFLAGS += @clock_gettime_ldflags@
+
man_pages := $(patsubst %, $(man_dir)/%.1, @executables@)
autocrap := config.h.in configure
@[ -z "$(Q)" ] || echo 'CC $<'
$(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @mad_cppflags@ $<
+$(object_dir)/compress_filter.o: compress_filter.c | $(object_dir)
+ @[ -z "$(Q)" ] || echo 'CC $<'
+ $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) -O3 $<
+
$(object_dir)/aacdec_filter.o: aacdec_filter.c | $(object_dir)
@[ -z "$(Q)" ] || echo 'CC $<'
$(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $<
para_recv: $(recv_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) $(recv_objs) -o $@ @recv_ldflags@
+ $(Q) $(CC) $(recv_objs) -o $@ @recv_ldflags@ $(LDFLAGS)
para_filter: $(filter_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) $(filter_objs) -o $@ @filter_ldflags@
+ $(Q) $(CC) $(filter_objs) -o $@ @filter_ldflags@ $(LDFLAGS)
para_client: $(client_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(client_objs) @client_ldflags@
+ $(Q) $(CC) -o $@ $(client_objs) @client_ldflags@ $(LDFLAGS)
para_gui: $(gui_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(gui_objs) -lcurses
+ $(Q) $(CC) -o $@ $(gui_objs) @gui_ldflags@ $(LDFLAGS)
para_audiod: $(audiod_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(audiod_objs) @audiod_ldflags@
+ $(Q) $(CC) -o $@ $(audiod_objs) @audiod_ldflags@ $(LDFLAGS)
para_audioc: $(audioc_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(audioc_objs) @audioc_ldflags@
+ $(Q) $(CC) -o $@ $(audioc_objs) @audioc_ldflags@ $(LDFLAGS)
para_fade: $(fade_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(fade_objs) @fade_ldflags@
+ $(Q) $(CC) -o $@ $(fade_objs) @fade_ldflags@ $(LDFLAGS)
para_server: $(server_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(server_objs) @server_ldflags@
+ $(Q) $(CC) -o $@ $(server_objs) @server_ldflags@ $(LDFLAGS)
para_write: $(write_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(write_objs) @write_ldflags@
+ $(Q) $(CC) -o $@ $(write_objs) @write_ldflags@ $(LDFLAGS)
para_afh: $(afh_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(afh_objs) @afh_ldflags@
+ $(Q) $(CC) -o $@ $(afh_objs) @afh_ldflags@ $(LDFLAGS)
para_play: $(play_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(play_objs) @play_ldflags@
+ $(Q) $(CC) -o $@ $(play_objs) @play_ldflags@ $(LDFLAGS)
clean:
@[ -z "$(Q)" ] || echo 'CLEAN'
-------------------------------------------
-0.?.? (to be announced) "spectral gravity"
-------------------------------------------
+---------------------------------------------
+0.5.0 (to be announced) "invertible validity"
+---------------------------------------------
+
+ - The sideband compatibility code has been removed, hence
+ sideband connections (introduced in 0.4.11) are now mandatory.
+ - Addblob commands can produce output.
+ - The stat command no longer sends garbage when para_server was
+ compiled against libgcrypt.
+
+--------------------------------------
+0.4.13 (2013-07-29) "spectral gravity"
+--------------------------------------
+
+One more 0.4.x release before the API-breaking changes for 0.5.0 go
+in. The main features of this release are the ogg/opus audio format,
+and UTF-8 support, but it includes also tons of other improvements
+and fixes all over the place.
+
+ - New audio format: ogg/opus.
+ - UTF8 support for para_gui and the mp3 audio format handler.
+ - Scheduler improvements and fixes.
+ - The obsolete gettimeofday() function has been replaced
+ by clock_gettime() on systems which support it.
+ - Speed and usability improvements for para_gui.
+ - para_client now restores the fd flags of stdin and stdout
+ on shutdown.
+ - Improved manual pages.
+ - Consistent version strings for all executables.
+ - Reduced dependencies on generated files result in fewer
+ recompilations on changes.
+ - Performance improvements for the compress filter.
+ - Improved downloads web page.
-----------------------------------------
0.4.12 (2012-12-20) "volatile relativity"
fn->private_data = NULL;
}
-static void aacdec_post_select(__a_unused struct sched *s, struct task *t)
+static int aacdec_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct btr_node *btrn = fn->btrn;
size_t len, skip, consumed, loaded;
next_buffer:
- t->error = 0;
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (ret < 0)
goto err;
if (ret == 0)
- return;
+ return 0;
btr_merge(btrn, fn->min_iqs);
len = btr_next_buffer(btrn, (char **)&inbuf);
len = PARA_MIN(len, (size_t)8192);
ret = -E_AACDEC_INIT;
if (NeAACDecInit2(padd->handle, p,
padd->decoder_length, &rate,
- &channels) < 0)
+ &channels) != 0)
goto out;
}
padd->sample_rate = rate;
}
err:
assert(ret < 0);
- t->error = ret;
btr_remove_node(&fn->btrn);
+ return ret;
}
/**
/** \file afh.c Paraslash's standalone audio format handler tool. */
#include <regex.h>
-#include <sys/time.h>
#include "para.h"
#include "string.h"
#include "afh.h"
#include "error.h"
#include "version.h"
+#include "ggo.h"
static struct afh_args_info conf;
INIT_AFH_ERRLISTS;
}
}
+__noreturn static void print_help_and_die(void)
+{
+ struct ggo_help h = DEFINE_GGO_HELP(afh);
+ int d = conf.detailed_help_given;
+ unsigned flags = d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
+
+ ggo_print_help(&h, flags);
+ printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
+ exit(EXIT_SUCCESS);
+}
+
/**
* The main function of para_afh.
*
struct afh_info afhi;
afh_cmdline_parser(argc, argv, &conf);
- HANDLE_VERSION_FLAG("afh", conf);
loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ version_handle_flag("afh", conf.version_given);
+ if (conf.help_given || conf.detailed_help_given)
+ print_help_and_die();
+ afh_init();
ret = -E_AFH_SYNTAX;
if (conf.inputs_num == 0)
goto out;
- afh_init();
for (i = 0; i < conf.inputs_num; i++) {
int ret2;
ret = mmap_full_file(conf.inputs[i], O_RDONLY, &audio_file_data,
/** \file afh_common.c Common audio format handler functions. */
#include <sys/mman.h> /* mmap */
-#include <sys/time.h> /* gettimeofday */
#include <sys/types.h>
#include <regex.h>
void flac_afh_init(struct audio_format_handler *);
#endif
+#ifdef HAVE_OPUS
+ void opus_afh_init(struct audio_format_handler *);
+#endif
+
void wma_afh_init(struct audio_format_handler *);
/** The list of all status items */
.name = "flac",
#ifdef HAVE_FLAC
.init = flac_afh_init,
+#endif
+ },
+ {
+ .name = "opus",
+#ifdef HAVE_OPUS
+ .init = opus_afh_init,
#endif
},
{
{
int i;
- PARA_INFO_LOG("supported audio formats: %s\n",
- SERVER_AUDIO_FORMATS);
+ PARA_INFO_LOG("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
FOR_EACH_AUDIO_FORMAT(i) {
PARA_NOTICE_LOG("initializing %s handler\n",
audio_format_name(i));
{
struct afh_recv_args_info *tmp = para_calloc(sizeof(*tmp));
- if (!afh_recv_cmdline_parser(argc, argv, tmp))
- return tmp;
- free(tmp);
- return NULL;
+ afh_recv_cmdline_parser(argc, argv, tmp);
+ return tmp;
}
static void afh_recv_free_config(void *conf)
sched_request_barrier_or_min_delay(&chunk_time, s);
}
-static void afh_recv_post_select(__a_unused struct sched *s, struct task *t)
+static int afh_recv_post_select(__a_unused struct sched *s, struct task *t)
{
struct receiver_node *rn = container_of(t, struct receiver_node, task);
struct afh_recv_args_info *conf = rn->conf;
btr_remove_node(&rn->btrn);
pard->current_chunk = pard->first_chunk;
}
- t->error = ret;
+ return ret;
}
/**
r->parse_config = afh_recv_parse_config;
r->free_config = afh_recv_free_config;
r->execute = afh_execute;
- r->help = (struct ggo_help) {
- .short_help = afh_recv_args_info_help,
- .detailed_help = afh_recv_args_info_detailed_help
- };
+ r->help = (struct ggo_help)DEFINE_GGO_HELP(afh_recv);
afh_recv_cmdline_parser_free(&dummy);
}
return 1;
}
+/**
+ * Result handler for sending data to the para_client process.
+ *
+ * \param result The data to be sent.
+ * \param band The band designator.
+ * \param private Pointer to the command context.
+ *
+ * \return The return value of the underlying call to \ref command.c::send_sb.
+ *
+ * \sa \ref callback_result_handler, \ref command.c::send_sb.
+ */
+int afs_cb_result_handler(struct osl_object *result, uint8_t band,
+ void *private)
+{
+ struct command_context *cc = private;
+
+ assert(cc);
+ if (!result->size)
+ return 1;
+ return send_sb(&cc->scc, result->data, result->size, band, true);
+}
+
static void com_select_callback(int fd, const struct osl_object *query)
{
struct para_buffer pb = {
free(pb.buf);
}
-/**
- * Result handler for sending data to the para_client process.
- *
- * \param result The data to be sent.
- * \param band The band designator.
- * \param private Pointer to the command context.
- *
- * \return The return value of the underlying call to \ref command.c::send_sb.
- *
- * \sa \ref callback_result_handler, \ref command.c::send_sb.
- */
-int afs_cb_result_handler(struct osl_object *result, uint8_t band,
- void *private)
-{
- struct command_context *cc = private;
-
- assert(cc);
- if (!result->size)
- return 1;
- return send_sb(&cc->scc, result->data, result->size, band, true);
-}
-
int com_select(struct command_context *cc)
{
struct osl_object query;
para_fd_set(st->fd, &s->rfds, &s->max_fileno);
}
-static void afs_signal_post_select(struct sched *s, struct task *t)
+static int afs_signal_post_select(struct sched *s, __a_unused struct task *t)
{
- int signum;
+ int signum, ret;
if (getppid() == 1) {
PARA_EMERG_LOG("para_server died\n");
}
signum = para_next_signal(&s->rfds);
if (signum == 0)
- return;
+ return 0;
if (signum == SIGHUP) {
close_afs_tables();
parse_config_or_die(1);
- t->error = open_afs_tables();
- if (t->error < 0)
- return;
+ ret = open_afs_tables();
+ if (ret < 0)
+ return ret;
init_admissible_files(current_mop);
- return;
+ return 0;
}
PARA_EMERG_LOG("terminating on signal %d\n", signum);
shutdown:
task_notify_all(s, E_AFS_SIGNAL);
- t->error = -E_AFS_SIGNAL;
+ return -E_AFS_SIGNAL;
}
static void register_signal_task(struct sched *s)
/** Shutdown connection if query has not arrived until this many seconds. */
#define AFS_CLIENT_TIMEOUT 3
-static void command_post_select(struct sched *s, struct task *t)
+static int command_post_select(struct sched *s, struct task *t)
{
struct command_task *ct = container_of(t, struct command_task, task);
struct sockaddr_un unix_addr;
int fd, ret;
ret = task_get_notification(t);
- if (ret < 0) {
- t->error = ret;
- return;
- }
+ if (ret < 0)
+ return ret;
ret = execute_server_command(&s->rfds);
if (ret < 0) {
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
task_notify_all(s, -ret);
- t->error = ret;
- return;
+ return ret;
}
/* Check the list of connected clients. */
list_for_each_entry_safe(client, tmp, &afs_client_list, node) {
if (ret < 0)
PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
if (ret <= 0)
- return;
+ return 0;
ret = mark_fd_nonblocking(fd);
if (ret < 0) {
PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
close(fd);
- return;
+ return 0;
}
client = para_malloc(sizeof(*client));
client->fd = fd;
client->connect_time = *now;
para_list_add(&client->node, &afs_client_list);
+ return 0;
}
static void register_command_task(uint32_t cookie, struct sched *s)
H: no amplification, 64 means the amplitude should be multiplied
H: by a factor of two, 128 by three and so on.
H:
-H: This value is used by the compress filter.
+H: This value is used by the amp filter.
H:
H: -v Verbose mode. Explain what is being done.
H:
.column_descriptions = aft_cols
};
-/* We don't want * dot or dot-dot anywhere. */
+/* We don't want dot or dot-dot anywhere. */
static int verify_dotfile(const char *rest)
{
/*
uint16_t afhi_offset, chunks_offset;
hash = (unsigned char *)buf + CAB_HASH_OFFSET;
- hash_to_asc(hash, asc);;
+ hash_to_asc(hash, asc);
objs[AFTCOL_HASH].data = buf + CAB_HASH_OFFSET;
objs[AFTCOL_HASH].size = HASH_SIZE;
#include <regex.h>
#include <sys/types.h>
#include <alsa/asoundlib.h>
-#include <sys/time.h>
#include "para.h"
#include "fd.h"
goto fail;
ret = snd_output_buffer_open(&output_log);
if (ret == 0) {
- char *buf;
+ char *buf, *p;
+ size_t sz;
PARA_INFO_LOG("dumping alsa configuration\n");
snd_pcm_dump(pad->handle, output_log);
- snd_output_buffer_string(output_log, &buf);
- for (;;) {
- char *p = strchr(buf, '\n');
- if (!p) /* omit last output line, it's empty */
+ 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;
- *p = '\0';
- PARA_INFO_LOG("%s\n", buf);
- buf = p + 1;
+ *q = '\0';
+ PARA_INFO_LOG("%s\n", p);
+ p = q + 1;
}
snd_output_close(output_log);
}
free(pad);
}
-static void alsa_write_post_select(__a_unused struct sched *s,
+static int alsa_write_post_select(__a_unused struct sched *s,
struct task *t)
{
struct writer_node *wn = container_of(t, struct writer_node, task);
again:
ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
if (ret == 0)
- return;
+ return 0;
btr_merge(btrn, wn->min_iqs);
bytes = btr_next_buffer(btrn, &data);
if (ret < 0 || bytes < wn->min_iqs) { /* eof */
PARA_DEBUG_LOG("waiting for device to drain\n");
tv_add(now, &(struct timeval)EMBRACE(0, 200 * 1000),
&pad->drain_barrier);
- return;
+ return 0;
}
if (tv_diff(now, &pad->drain_barrier, NULL) > 0)
goto err;
- return;
+ return 0;
}
if (!pad) {
int32_t val;
if (bytes == 0) /* no data available */
- return;
+ return 0;
pad = para_calloc(sizeof(*pad));
get_btr_sample_rate(btrn, &val);
pad->sample_rate = val;
goto again;
}
if (pad->poll_fd < 0 || !FD_ISSET(pad->poll_fd, &s->rfds))
- return;
+ return 0;
frames = bytes / pad->bytes_per_frame;
frames = snd_pcm_writei(pad->handle, data, frames);
if (frames == 0 || frames == -EAGAIN) {
char buf[100];
if (read(pad->poll_fd, buf, 100))
do_nothing;
- return;
+ return 0;
}
if (frames > 0) {
btr_consume(btrn, frames * pad->bytes_per_frame);
if (frames == -EPIPE) {
PARA_WARNING_LOG("underrun (tried to write %zu bytes)\n", bytes);
snd_pcm_prepare(pad->handle);
- return;
+ return 0;
}
PARA_ERROR_LOG("alsa write error: %s\n", snd_strerror(-frames));
ret = -E_ALSA;
err:
assert(ret < 0);
btr_remove_node(&wn->btrn);
- t->error = ret;
+ return ret;
}
__malloc static void *alsa_parse_config_or_die(int argc, char **argv)
w->post_select = alsa_write_post_select;
w->parse_config_or_die = alsa_parse_config_or_die;
w->free_config = alsa_free_config;
- w->help = (struct ggo_help) {
- .short_help = alsa_write_args_info_help,
- .detailed_help = alsa_write_args_info_detailed_help
- };
+ w->help = (struct ggo_help)DEFINE_GGO_HELP(alsa_write);
alsa_write_cmdline_parser_free(&dummy);
}
/** Data specific to the amplify filter. */
struct private_amp_data {
- /** Points to the configuration data for this instance of this filter. */
/** Amplification factor. */
unsigned amp;
};
pad->amp, pad->amp / 64.0 + 1.0);
}
-static void amp_post_select(__a_unused struct sched *s, struct task *t)
+static int amp_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct private_amp_data *pad = fn->private_data;
bool inplace = btr_inplace_ok(btrn);
if (pad->amp == 0) { /* no amplification */
- t->error = -E_AMP_ZERO_AMP;
btr_splice_out_node(btrn);
- return;
+ return -E_AMP_ZERO_AMP;
}
next_buffer:
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (ret < 0)
goto err;
if (ret == 0)
- return;
+ return 0;
btr_merge(btrn, fn->min_iqs);
in_bytes = btr_next_buffer(btrn, (char **)&in);
len = in_bytes / 2;
btr_consume(btrn, len * 2);
btr_add_output((char *)out, len * 2, btrn);
}
- t->error = 0;
goto next_buffer;
err:
assert(ret < 0);
- t->error = ret;
btr_remove_node(&fn->btrn);
+ return ret;
}
static void amp_free_config(void *conf)
f->post_select = amp_post_select;
f->parse_config = amp_parse_config;
f->free_config = amp_free_config;
- f->help = (struct ggo_help) {
- .short_help = amp_filter_args_info_help,
- .detailed_help = amp_filter_args_info_detailed_help
- };
+ f->help = (struct ggo_help)DEFINE_GGO_HELP(amp_filter);
}
return -E_AO_PTHREAD;
}
-static void aow_post_select(__a_unused struct sched *s,
+static int aow_post_select(__a_unused struct sched *s,
struct task *t)
{
struct writer_node *wn = container_of(t, struct writer_node, task);
if (ret < 0)
goto remove_btrn;
if (ret == 0)
- return;
+ return 0;
get_btr_sample_rate(wn->btrn, &rate);
get_btr_channels(wn->btrn, &ch);
get_btr_sample_format(wn->btrn, &format);
ret = aow_create_thread(wn);
if (ret < 0)
goto remove_thread_btrn;
- return;
+ return 0;
}
pthread_mutex_lock(&pawd->mutex);
ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
remove_btrn:
btr_remove_node(&wn->btrn);
out:
- t->error = ret;
+ return ret;
}
__malloc static void *aow_parse_config_or_die(int argc, char **argv)
w->post_select = aow_post_select;
w->parse_config_or_die = aow_parse_config_or_die;
w->free_config = aow_free_config;
- w->help = (struct ggo_help) {
- .short_help = ao_write_args_info_help,
- };
+ w->help = (struct ggo_help)DEFINE_GGO_HELP(ao_write);
/* create detailed help containing all supported drivers/options */
for (i = 0; ao_write_args_info_detailed_help[i]; i++)
; /* nothing */
#include "net.h"
#include "string.h"
#include "fd.h"
+#include "ggo.h"
#include "version.h"
INIT_AUDIOC_ERRLISTS;
return buf;
}
+static int connect_audiod(const char *sname, char *args)
+{
+ int fd = -1, ret;
+
+ ret = connect_local_socket(sname);
+ if (ret < 0)
+ goto fail;
+ fd = ret;
+ ret = send_cred_buffer(fd, args);
+ if (ret < 0)
+ goto fail;
+ return fd;
+fail:
+ PARA_ERROR_LOG("could not connect %s\n", sname);
+ if (fd >= 0)
+ close(fd);
+ return ret;
+}
+
#ifdef HAVE_READLINE
#include "list.h"
#include "sched.h"
para_fd_set(at->fd, &s->rfds, &s->max_fileno);
}
-static void audioc_post_select(struct sched *s, struct task *t)
+static int audioc_post_select(struct sched *s, struct task *t)
{
char *buf = NULL;
struct audioc_task *at = container_of(t, struct audioc_task, task);
if (ret < 0)
goto out;
if (!FD_ISSET(at->fd, &s->rfds))
- return;
+ return 0;
buf = para_malloc(conf.bufsize_arg);
ret = recv_bin_buffer(at->fd, buf, conf.bufsize_arg);
PARA_DEBUG_LOG("recv: %d\n", ret);
if (ret < 0)
goto out;
btr_add_output(buf, ret, at->btrn);
- return;
+ return 0;
out:
if (ret < 0) {
free(buf);
btr_remove_node(&at->btrn);
close(at->fd);
}
- t->error = ret;
+ return ret;
}
static struct audioc_task audioc_task = {
{
char *args = NULL;
int ret;
- if (!line || !*line)
- return 0;
PARA_DEBUG_LOG("line: %s\n", line);
ret = create_argv(line, " ", &conf.inputs);
conf.inputs_num = ret;
args = concat_args(conf.inputs_num, conf.inputs);
free_argv(conf.inputs);
+ if (!args)
+ return 0;
conf.inputs_num = 0; /* required for audioc_cmdline_parser_free() */
- ret = connect_local_socket(socket_name);
+ ret = connect_audiod(socket_name, args);
if (ret < 0)
goto out;
at->fd = ret;
ret = mark_fd_nonblocking(at->fd);
- if (ret < 0)
- goto close;
- ret = send_cred_buffer(at->fd, args);
if (ret < 0)
goto close;
free(args);
.loglevel = loglevel,
.completers = audiod_completers,
};
- PARA_NOTICE_LOG("\n%s\n", VERSION_TEXT("audioc"));
+ PARA_NOTICE_LOG("\n%s\n", version_text("audioc"));
if (conf.history_file_given)
history_file = para_strdup(conf.history_file_arg);
else {
return NULL;
}
+__noreturn static void print_help_and_die(void)
+{
+ struct ggo_help h = DEFINE_GGO_HELP(audioc);
+ bool d = conf.detailed_help_given;
+
+ ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+ exit(0);
+}
+
/**
* The client program to connect to para_audiod.
*
*/
int main(int argc, char *argv[])
{
- int ret = -E_AUDIOC_SYNTAX, fd;
+ int ret, fd;
char *cf, *buf = NULL, *args = NULL;
size_t bufsize;
- if (audioc_cmdline_parser(argc, argv, &conf))
- goto out;
- HANDLE_VERSION_FLAG("audioc", conf);
+ audioc_cmdline_parser(argc, argv, &conf);
+ loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ version_handle_flag("audioc", conf.version_given);
+ if (conf.help_given || conf.detailed_help_given)
+ print_help_and_die();
cf = configfile_exists();
if (cf) {
struct audioc_cmdline_parser_params params = {
.override = 0,
.initialize = 0,
.check_required = 0,
- .check_ambiguity = 0
+ .check_ambiguity = 0,
+ .print_errors = 1,
+
};
- ret = audioc_cmdline_parser_config_file(cf, &conf, ¶ms);
+ audioc_cmdline_parser_config_file(cf, &conf, ¶ms);
free(cf);
- if (ret) {
- fprintf(stderr, "parse error in config file\n");
- exit(EXIT_FAILURE);
- }
+ loglevel = get_loglevel_by_name(conf.loglevel_arg);
}
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
if (conf.socket_given)
socket_name = para_strdup(conf.socket_arg);
else {
interactive_session();
args = concat_args(conf.inputs_num, conf.inputs);
- ret = connect_local_socket(socket_name);
+ ret = connect_audiod(socket_name, args);
free(socket_name);
- if (ret < 0) {
- PARA_EMERG_LOG("failed to connect to local socket\n");
+ if (ret < 0)
goto out;
- }
fd = ret;
- ret = send_cred_buffer(fd, args);
+ ret = mark_fd_blocking(STDOUT_FILENO);
if (ret < 0)
goto out;
bufsize = conf.bufsize_arg;
buf = para_malloc(bufsize);
- ret = mark_fd_blocking(STDOUT_FILENO);
- if (ret < 0)
- goto out;
do {
size_t n = ret = recv_bin_buffer(fd, buf, bufsize);
if (ret <= 0)
PARA_EMERG_LOG("can not read config file %s\n", config_file);
goto err;
}
- if (ret)
+ if (ret) {
audiod_cmdline_parser_config_file(config_file, &conf, ¶ms);
+ daemon_set_loglevel(conf.loglevel_arg);
+ }
free(config_file);
- daemon_set_loglevel(conf.loglevel_arg);
return;
err:
free(config_file);
struct audio_format_info *a = afi + s->format;
if (a->num_filters == 0)
return;
- for (i = 0; i < a->num_filters; i++) {
+ for (i = a->num_filters - 1; i >= 0; i--) {
struct filter_node *fn = s->fns + i;
struct filter *f;
fn->conf = a->filter_conf[i];
fn->task.pre_select = f->pre_select;
fn->task.post_select = f->post_select;
-
fn->btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = f->name, .parent = parent,
.handler = f->execute, .context = fn));
para_fd_set(st->fd, &s->rfds, &s->max_fileno);
}
-static void signal_post_select(struct sched *s, __a_unused struct task *t)
+static int signal_post_select(struct sched *s, __a_unused struct task *t)
{
int signum;
PARA_EMERG_LOG("terminating on signal %d\n", signum);
clean_exit(EXIT_FAILURE, "caught deadly signal");
}
+ return 0;
}
static void signal_setup_default(struct signal_task *st)
para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
}
-static void command_post_select(struct sched *s, struct task *t)
+static int command_post_select(struct sched *s, struct task *t)
{
int ret;
struct command_task *ct = container_of(t, struct command_task, task);
if (ret < 0)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
audiod_status_dump();
+ return 0;
}
static void init_command_task(struct command_task *ct)
tv_add(now, &delay, &stat_task->restart_barrier);
}
-static void try_to_close_slot(int slot_num)
+static bool must_close_slot(int slot_num)
{
struct slot_info *s = &slot[slot_num];
struct audio_format_info *a = afi + s->format;
int i;
if (s->format < 0)
- return;
+ return false;
if (s->receiver_node && s->receiver_node->task.error >= 0)
- return;
+ return false;
for (i = 0; i < a->num_filters; i++)
if (s->fns && s->fns[i].task.error >= 0)
- return;
+ return false;
if (a->num_writers > 0) {
for (i = 0; i < a->num_writers; i++)
if (s->wns && s->wns[i].task.error >= 0)
- return;
+ return false;
} else {
if (s->wns && s->wns[0].task.error >= 0)
- return;
+ return false;
+ }
+ return true;
+}
+
+static void close_unused_slots(void)
+{
+ int i;
+
+ FOR_EACH_SLOT(i) {
+ struct slot_info *s = slot + i;
+ if (!must_close_slot(i))
+ continue;
+ PARA_INFO_LOG("closing slot %d\n", i);
+ close_writers(s);
+ close_filters(s);
+ close_receiver(i);
+ clear_slot(i);
}
- PARA_INFO_LOG("closing slot %d\n", slot_num);
- close_writers(s);
- close_filters(s);
- close_receiver(slot_num);
- clear_slot(slot_num);
}
/*
*/
static void start_stop_decoders(void)
{
- int i, ret;
+ int ret;
struct slot_info *sl;
struct audio_format_info *a;
- FOR_EACH_SLOT(i)
- try_to_close_slot(i);
+ close_unused_slots();
if (audiod_status != AUDIOD_ON ||
!(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING))
return notify_receivers(E_NOT_PLAYING);
static void status_pre_select(struct sched *s, struct task *t)
{
struct status_task *st = container_of(t, struct status_task, task);
- int ret, cafn = stat_task->current_audio_format_num;
+ int i, ret, cafn = stat_task->current_audio_format_num;
if (must_start_decoder())
goto min_delay;
+ FOR_EACH_SLOT(i)
+ if (must_close_slot(i))
+ goto min_delay;
ret = btr_node_status(st->btrn, st->min_iqs, BTR_NT_LEAF);
if (ret > 0)
goto min_delay;
}
/* restart the client task if necessary */
-static void status_post_select(struct sched *s, struct task *t)
+static int status_post_select(struct sched *s, struct task *t)
{
struct status_task *st = container_of(t, struct status_task, task);
st->last_status_read = *now;
out:
start_stop_decoders();
+ return 0;
}
static void init_status_task(struct status_task *st)
__noreturn static void print_help_and_die(void)
{
- int d = conf.detailed_help_given;
- const char **p = d? audiod_args_info_detailed_help
- : audiod_args_info_help;
-
- printf_or_die("%s\n\n", AUDIOD_CMDLINE_PARSER_PACKAGE "-"
- AUDIOD_CMDLINE_PARSER_VERSION);
- printf_or_die("%s\n\n", audiod_args_info_usage);
- for (; *p; p++)
- printf_or_die("%s\n", *p);
- print_receiver_helps(d);
- print_filter_helps(d);
- print_writer_helps(d);
+ struct ggo_help h = DEFINE_GGO_HELP(audiod);
+ bool d = conf.detailed_help_given;
+ unsigned flags;
+
+ flags = d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
+ ggo_print_help(&h, flags);
+
+ flags = d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS;
+ print_receiver_helps(flags);
+ print_filter_helps(flags);
+ print_writer_helps(flags);
exit(0);
}
};
valid_fd_012();
- if (audiod_cmdline_parser_ext(argc, argv, &conf, ¶ms))
- exit(EXIT_FAILURE);
- HANDLE_VERSION_FLAG("audiod", conf);
+ audiod_cmdline_parser_ext(argc, argv, &conf, ¶ms);
+ daemon_set_loglevel(conf.loglevel_arg);
+ version_handle_flag("audiod", conf.version_given);
/* init receivers/filters/writers early to make help work */
recv_init();
filter_init();
if (ret < 0)
goto out;
ret = create_argv(buf, "\n", &argv);
- if (ret < 0)
+ if (ret <= 0)
goto out;
argc = ret;
//PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc);
{
struct btr_buffer *btrb;
- assert(size != 0);
+ if (size == 0)
+ return;
if (list_empty(&btrn->children)) {
free(buf);
return;
{
struct btr_buffer *btrb;
- assert(size != 0);
+ if (size == 0)
+ return;
if (list_empty(&btrn->children))
return;
btrb = new_btrb((char *)buf, size);
char *buf;
size_t avail;
- assert(size != 0);
+ if (size == 0)
+ return;
if (list_empty(&btrn->children))
return;
avail = btr_pool_get_buffer(btrp, &buf);
*tv = btrn->start;
}
+/**
+ * Get the parent node of a buffer tree node.
+ *
+ * \param btrn The node whose parent should be returned.
+ *
+ * \a btrn must not be \p NULL.
+ *
+ * \return The parent of \a btrn, or \p NULL if \a btrn is the
+ * root node of the buffer tree.
+ */
struct btr_node *btr_parent(struct btr_node *btrn)
{
return btrn->parent;
/** Length of a standard wav header. */
#define WAV_HEADER_LEN 44
+/** The possible states of a check_wav instance. */
enum check_wav_state {
+ /** Initial state, less than \p WAV_HEADER_LEN bytes available. */
CWS_NEED_HEADER,
+ /** Wav hader was detected. */
CWS_HAVE_HEADER,
+ /** First part of the stream did not look like a wav header. */
CWS_NO_HEADER,
};
unsigned sample_rate;
};
+/**
+ * Set select timeout according to the given context.
+ *
+ * \param s Contains the timeval that should be set.
+ * \param cwc Contains a pointer to the buffer tree node.
+ *
+ * This requests a minimal timeout from the scheduler if btrn of \a cwc is not
+ * idle.
+ */
void check_wav_pre_select(struct sched *s, struct check_wav_context *cwc)
{
int ret = btr_node_status(cwc->btrn, cwc->min_iqs, BTR_NT_INTERNAL);
return 1;
}
+/**
+ * Filter out the wav header, pushdown everything else.
+ *
+ * \param cwc The context of this instance.
+ *
+ * This function looks at the first \p WAV_HEADER_SIZE bytes of the input queue
+ * of the btrn of \a cwc. If they look like a wav header, the function extracts
+ * the information of interest and swallows this part of the stream. Otherwise
+ * it is pushed down to all children. In either case the rest of the input is
+ * pushed down as well.
+ *
+ * Once the first part has been processed this way, the state of the instance
+ * changes from \p CWS_NEED_HEADER to \p CWS_HAVE_HEADER or \p CWS_NO_HEADER.
+ *
+ * \return Standard.
+ */
int check_wav_post_select(struct check_wav_context *cwc)
{
struct btr_node *btrn = cwc->btrn;
return ret;
}
+/**
+ * Allocate and set up a new check_wav instance.
+ *
+ * \param parent This buffer tree node will be the parent of the new node.
+ * \param child The child of the new node.
+ * \param params Default values and options.
+ * \param cw_btrn A pointer to the check wav node is returned here.
+ *
+ * This function also sets up the ->execute handler of the btrn so that all
+ * children of this node can figure out channel count, sample rate, etc.
+ *
+ * \return The (opaque) handle of the newly created check_wav instance. It is
+ * supposed to be passed to \ref check_wav_pre_select() and \ref
+ * check_wav_post_select().
+ *
+ * \sa \ref btr_new_node.
+ */
struct check_wav_context *check_wav_init(struct btr_node *parent,
struct btr_node *child, struct wav_params *params,
struct btr_node **cw_btrn)
return cwc;
}
+/**
+ * Dellocate all ressources of a check_wav instance.
+ *
+ * \param cwc Determines the instance to shut down.
+ *
+ * This function may only be called after check_wav_post_select() has returned
+ * negative.
+ */
void check_wav_shutdown(struct check_wav_context *cwc)
{
free(cwc);
+/*
+ * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file check_wav.h Detect, process and cut a wav header. */
+
struct check_wav_context;
/**
sched_min_delay(s);
}
-static void exec_post_select(__a_unused struct sched *s, struct task *t)
+static int exec_post_select(__a_unused struct sched *s, struct task *t)
{
struct exec_task *et = container_of(t, struct exec_task, task);
struct btr_node *btrn = et->btrn;
int ret;
ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
- if (ret <= 0) {
- t->error = ret;
- return;
- }
+ if (ret <= 0)
+ return ret;
sz = btr_next_buffer(btrn, &buf);
if (sz <= 1)
goto out;
et->result_buf[et->result_size - 1] = '\0';
out:
btr_consume(btrn, sz);
+ return 0;
}
static int make_client_argv(const char *line)
int ret;
client_disconnect(ct);
- if (!line || !*line)
- return 0;
- PARA_DEBUG_LOG("line handler: %s\n", line);
+ PARA_DEBUG_LOG("line: %s\n", line);
ret = make_client_argv(line);
- if (ret < 0)
+ if (ret <= 0)
return ret;
ret = client_connect(ct, &sched, NULL, NULL);
if (ret < 0)
.completers = completers,
};
- PARA_NOTICE_LOG("\n%s\n", VERSION_TEXT("client"));
+ PARA_NOTICE_LOG("\n%s\n", version_text("client"));
if (ct->conf.history_file_given)
history_file = para_strdup(ct->conf.history_file_arg);
else {
struct task task;
};
-static void supervisor_post_select(struct sched *s, struct task *t)
+static int supervisor_post_select(struct sched *s, struct task *t)
{
struct supervisor_task *svt = container_of(t, struct supervisor_task,
task);
- if (ct->task.error < 0) {
- t->error = ct->task.error;
- return;
- }
+ if (ct->task.error < 0)
+ return ct->task.error;
if (!svt->stdout_task_started && ct->status == CL_EXECUTING) {
stdout_set_defaults(&sot);
register_task(s, &sot.task);
svt->stdout_task_started = true;
- return;
+ return 1;
}
if (ct->status == CL_SENDING) {
stdin_set_defaults(&sit);
register_task(s, &sit.task);
- t->error = -E_TASK_STARTED;
- return;
+ return -E_TASK_STARTED;
}
+ return 0;
}
static struct supervisor_task supervisor_task = {
#include "error.h"
#include "list.h"
#include "sched.h"
-#include "client.cmdline.h"
#include "crypt.h"
#include "net.h"
#include "fd.h"
#include "client.h"
#include "buffer_tree.h"
#include "version.h"
+#include "ggo.h"
/** The size of the receiving buffer. */
#define CLIENT_BUFSIZE 4000
/**
- * Close the connection to para_server and deallocate per-command ressources.
+ * Close the connection to para_server and deallocate per-command resources.
*
* \param ct The client task.
*
- * This frees all ressources of the current command but keeps the configuration
+ * This frees all resources of the current command but keeps the configuration
* in \p ct->conf.
*
* \sa \ref client_close().
*
* \sa struct sched, struct task.
*/
-static void client_post_select(struct sched *s, struct task *t)
+static int client_post_select(struct sched *s, struct task *t)
{
struct client_task *ct = container_of(t, struct client_task, task);
int ret = 0;
if (ret < 0)
goto out;
if (ct->scc.fd < 0)
- return;
+ return 0;
switch (ct->status) {
case CL_CONNECTED: /* receive welcome message */
ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &s->rfds, &n);
goto out;
}
ct->status = CL_RECEIVED_WELCOME;
- return;
+ return 0;
case CL_RECEIVED_WELCOME: /* send auth command */
if (!FD_ISSET(ct->scc.fd, &s->wfds))
- return;
+ return 0;
sprintf(buf, AUTH_REQUEST_MSG "%s sideband", ct->user);
PARA_INFO_LOG("--> %s\n", buf);
ret = write_buffer(ct->scc.fd, buf);
if (ret < 0)
goto out;
ct->status = CL_SENT_AUTH;
- return;
+ return 0;
case CL_SENT_AUTH:
/*
* Receive challenge and session keys, decrypt the challenge and
hash_to_asc(ct->challenge_hash, buf);
PARA_INFO_LOG("--> %s\n", buf);
ct->status = CL_RECEIVED_CHALLENGE;
- return;
+ return 0;
}
case CL_RECEIVED_CHALLENGE:
ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE,
case CL_RECEIVED_PROCEED: /* concat args and send command */
{
if (!FD_ISSET(ct->scc.fd, &s->wfds))
- return;
+ return 0;
ret = send_sb_command(ct);
if (ret <= 0)
goto out;
ct->status = CL_EXECUTING;
- return;
+ return 0;
}
case CL_SENDING:
if (ct->btrn[1]) {
PARA_INFO_LOG("channel 1: %s\n", para_strerror(-ret));
btr_remove_node(&ct->btrn[1]);
if (ct->btrn[0])
- return;
+ return 0;
goto out;
close0:
PARA_INFO_LOG("channel 0: %s\n", para_strerror(-ret));
btr_remove_node(&ct->btrn[0]);
if (ct->btrn[1] && ct->status == CL_SENDING)
- return;
+ return 0;
out:
if (ret >= 0)
- return;
+ return 0;
btr_remove_node(&ct->btrn[0]);
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));
- t->error = ret;
+ return ret;
}
/**
return ret;
}
+__noreturn static void print_help_and_die(struct client_task *ct)
+{
+ struct ggo_help h = DEFINE_GGO_HELP(client);
+ bool d = ct->conf.detailed_help_given;
+
+ ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+ exit(0);
+}
+
/**
* Parse a client configuration.
*
ret = -E_CLIENT_SYNTAX;
if (client_cmdline_parser(argc, argv, &ct->conf))
goto out;
- HANDLE_VERSION_FLAG("client", ct->conf);
+ version_handle_flag("client", ct->conf.version_given);
+ if (ct->conf.help_given || ct->conf.detailed_help_given)
+ print_help_and_die(ct);
ct->config_file = ct->conf.config_file_given?
para_strdup(ct->conf.config_file_arg) :
#include <regex.h>
#include <signal.h>
-#include <sys/time.h>
#include <sys/types.h>
#include <osl.h>
localtime_r(&nmmd->mtime, &mtime_tm);
strftime(mtime, 29, "%b %d %Y", &mtime_tm);
}
- gettimeofday(¤t_time, NULL);
+ clock_get_realtime(¤t_time);
/*
* The calls to WRITE_STATUS_ITEM() below never fail because
* b->max_size is zero (unlimited), see para_printf(). However, clang
free(info);
}
ut = get_server_uptime_str(now);
- ret = xasprintf(&msg, "version: " GIT_VERSION "\n"
+ ret = xasprintf(&msg,
+ "version: %s\n"
"up: %s\nplayed: %u\n"
"server_pid: %d\n"
"afs_pid: %d\n"
"current loglevel: %s\n"
"supported audio formats: %s\n"
"%s",
+ version_git(),
ut, mmd->num_played,
(int)getppid(),
(int)mmd->afs_pid,
mmd->num_commands,
mmd->num_connects,
conf.loglevel_arg,
- SERVER_AUDIO_FORMATS,
+ AUDIO_FORMAT_HANDLERS,
sender_info
);
mutex_unlock(mmd_mutex);
if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
- msg = VERSION_TEXT("server") "built: " BUILD_DATE "\n" UNAME_RS
- ", " CC_VERSION "\n";
- len = strlen(msg);
- return send_sb(&cc->scc, msg, len, SBD_OUTPUT, true);
+ len = xasprintf(&msg, "%s", version_text("server"));
+ return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false);
}
#define EMPTY_STATUS_ITEMS \
*/
static unsigned empty_status_items(int parser_friendly, char **result)
{
- static char *esi;
- static unsigned len;
-
- if (esi)
- goto out;
+ char *esi;
+ unsigned len;
if (parser_friendly)
len = xasprintf(&esi,
EMPTY_STATUS_ITEMS
#undef ITEM
);
-out:
*result = esi;
return len;
}
{
int i, ret;
struct misc_meta_data tmp, *nmmd = &tmp;
- char *s, *esi = NULL;
+ char *s;
int32_t num = 0;
int parser_friendly = 0;
if (ret < 0)
goto out;
if (nmmd->vss_status_flags & VSS_NEXT) {
+ char *esi;
ret = empty_status_items(parser_friendly, &esi);
- ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT, true);
+ ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT, false);
if (ret < 0)
goto out;
} else
goto out;
}
out:
- free(esi);
return ret;
}
free(fn->private_data);
}
-static void compress_post_select(__a_unused struct sched *s, struct task *t)
+static int compress_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct private_compress_data *pcd = fn->private_data;
//inplace = false;
next_buffer:
- t->error = 0;
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (ret < 0)
goto err;
if (ret == 0)
- return;
+ return 0;
btr_merge(btrn, fn->min_iqs);
length = btr_next_buffer(btrn, &inbuf) & ~(size_t)1;
if (length == 0) { /* eof and 1 byte available */
op = para_malloc(length);
for (i = 0; i < length / 2; i++) {
/* be careful in that heat, my dear */
- int sample = *ip++, adjusted_sample = (PARA_ABS(sample) *
- pcd->current_gain) >> gain_shift;
- if (adjusted_sample > 32767) { /* clip */
- PARA_NOTICE_LOG("clip: sample: %d, adjusted sample: %d\n",
- sample, adjusted_sample);
- adjusted_sample = 32767;
+ int sample = *ip++;
+ bool neg = false;
+
+ if (sample < 0) {
+ sample = -sample;
+ neg = true;
+ }
+ sample *= pcd->current_gain;
+ sample >>= gain_shift;
+ if (sample > 32767) { /* clip */
+ sample = 32767;
pcd->current_gain = (3 * pcd->current_gain +
(1 << pcd->conf->inertia_arg)) / 4;
pcd->peak = 0;
- } else
- pcd->peak = PARA_MAX(pcd->peak, adjusted_sample);
- op[i] = sample >= 0? adjusted_sample : -adjusted_sample;
+ } else if (sample > pcd->peak)
+ pcd->peak = sample;
+ op[i] = neg? -sample : sample;
if (++pcd->num_samples & mask)
continue;
// PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain,
goto next_buffer;
err:
assert(ret < 0);
- t->error = ret;
btr_remove_node(&fn->btrn);
+ return ret;
}
/** TODO: Add sanity checks */
f->post_select = compress_post_select;
f->parse_config = compress_parse_config;
f->free_config = compress_free_config;
- f->help = (struct ggo_help) {
- .short_help = compress_filter_args_info_help,
- .detailed_help = compress_filter_args_info_detailed_help
- };
+ f->help = (struct ggo_help)DEFINE_GGO_HELP(compress_filter);
}
exec send_common ggo udp_recv color fec fecdec_filter
prebuffer_filter bitstream imdct check_wav
wma_afh wma_common wmadec_filter buffer_tree crypt_common
- gui gui_theme sideband afh_recv play"
+ gui gui_theme sideband afh_recv play version"
executables="recv filter audioc write client afh audiod play"
recv_errlist_objs="
http_recv recv_common recv time string net dccp_recv fd
sched stdout ggo udp_recv buffer_tree afh_recv afh_common
- wma_afh wma_common mp3_afh
+ wma_afh wma_common mp3_afh version
"
recv_ldflags=""
filter_cmdline_objs="add_cmdline(filter compress_filter amp_filter prebuffer_filter)"
filter_errlist_objs="filter_common wav_filter compress_filter filter string
- stdin stdout sched fd amp_filter ggo fecdec_filter fec
+ stdin stdout sched fd amp_filter ggo fecdec_filter fec version
prebuffer_filter time bitstream imdct wma_common wmadec_filter buffer_tree"
filter_ldflags="-lm"
filters=" compress wav amp fecdec wmadec prebuffer"
audioc_cmdline_objs="add_cmdline(audioc)"
-audioc_errlist_objs="audioc string net fd"
+audioc_errlist_objs="
+ audioc
+ string
+ net
+ fd
+ version
+ ggo
+"
audioc_ldflags=""
audiod_cmdline_objs="add_cmdline(audiod compress_filter http_recv dccp_recv file_write client amp_filter udp_recv prebuffer_filter)"
audiod_errlist_objs="audiod signal string daemon stat net crypt_common sideband
time grab_client filter_common wav_filter compress_filter amp_filter http_recv dccp_recv
recv_common fd sched write_common file_write audiod_command fecdec_filter
- client_common ggo udp_recv color fec prebuffer_filter
+ client_common ggo udp_recv color fec prebuffer_filter version
bitstream imdct wma_common wmadec_filter buffer_tree"
audiod_ldflags="-lm"
audiod_audio_formats="wma"
afh_cmdline_objs="add_cmdline(afh)"
-afh_errlist_objs="afh string fd mp3_afh afh_common time wma_afh wma_common"
+afh_errlist_objs="afh string fd mp3_afh afh_common time wma_afh wma_common
+ version ggo"
afh_ldflags=""
write_cmdline_objs="add_cmdline(write file_write)"
write_errlist_objs="write write_common file_write time fd string sched stdin
- buffer_tree ggo check_wav"
+ buffer_tree ggo check_wav version"
write_ldflags=""
writers=" file"
default_writer="FILE_WRITE"
client_cmdline_objs="add_cmdline(client)"
-client_errlist_objs="client net string fd sched stdin stdout time sideband
- client_common buffer_tree crypt_common"
+client_errlist_objs="
+ client
+ net
+ string
+ fd
+ sched
+ stdin
+ stdout
+ time
+ sideband
+ client_common
+ buffer_tree
+ crypt_common
+ version
+ ggo
+"
client_ldflags=""
gui_cmdline_objs="add_cmdline(gui)"
-gui_errlist_objs="exec signal string stat ringbuffer fd gui gui_theme"
+gui_errlist_objs="
+ exec
+ signal
+ string
+ stat
+ ringbuffer
+ fd
+ gui
+ gui_theme
+ time
+ version
+ ggo
+"
gui_objs="$gui_cmdline_objs $gui_errlist_objs"
play_errlist_objs="play fd sched ggo buffer_tree time string net
afh_recv afh_common
wav_filter compress_filter amp_filter prebuffer_filter fecdec_filter
wmadec_filter
write_common file_write
+ version
"
play_cmdline_objs="add_cmdline(http_recv dccp_recv udp_recv afh_recv compress_filter amp_filter prebuffer_filter file_write play)"
play_ldflags="-lm"
AC_MSG_ERROR([fatal: buggy snprintf() detected])
fi])
AX_FUNC_SNPRINTF()
+################################################################## clock_gettime
+clock_gettime_lib=
+AC_CHECK_LIB([c], [clock_gettime], [clock_gettime_lib=c], [
+ AC_CHECK_LIB([rt], [clock_gettime], [clock_gettime_lib=rt], [], [])
+])
+if test -n "$clock_gettime_lib"; then
+ AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [
+ define to 1 if clock_gettime() is supported])
+fi
+if test "$clock_gettime_lib" = "rt"; then
+ AC_SUBST(clock_gettime_ldflags, -lrt)
+fi
########################################################################### osl
have_osl=yes
OLD_CPPFLAGS="$CPPFLAGS"
extras="$extras server"
executables="$executables server"
server_cmdline_objs="add_cmdline(server)"
- server_errlist_objs="server afh_common mp3_afh vss command net
- string signal time daemon http_send close_on_fork mm
- crypt_common ipc dccp_send fd user_list chunk_queue
- afs aft mood score attribute blob playlist sched acl
- send_common udp_send color fec wma_afh wma_common sideband"
+ server_errlist_objs="
+ server
+ afh_common
+ mp3_afh
+ vss command
+ net
+ string
+ signal
+ time
+ daemon
+ http_send
+ close_on_fork
+ mm
+ crypt_common
+ ipc dccp_send
+ fd
+ user_list
+ chunk_queue
+ afs
+ aft
+ mood
+ score
+ attribute
+ blob
+ playlist
+ sched
+ acl
+ send_common
+ udp_send
+ color
+ fec
+ wma_afh
+ wma_common
+ sideband
+ version
+ ggo
+ "
all_errlist_objs="$all_errlist_objs server vss command
http_send close_on_fork mm ipc dccp_send user_list
chunk_queue afs aft mood score attribute blob playlist
acl udp_send"
server_ldflags=""
- server_audio_formats="mp3 wma"
+ audio_format_handlers="mp3 wma"
AC_SUBST(osl_cppflags)
server_ldflags="$server_ldflags $osl_libs -losl"
fi
if test ${have_ucred} = yes; then
AC_DEFINE(HAVE_UCRED, 1, define to 1 you have struct ucred)
fi
-
+########################################################################### gengetopt
+echo 'option "z" z "" flag off' | $gengetopt --file-name conftest-ggo &&
+AC_CHECK_DECL(
+ [gengetopt_args_info_description],
+ [ggo_descriptions_declared=yes],
+ [ggo_descriptions_declared=no],
+ [#include "conftest-ggo.h"]
+)
+AC_SUBST(ggo_descriptions_declared)
########################################################################### curses
have_curses="yes"
OLD_CPPFLAGS="$CPPFLAGS"
AC_CHECK_HEADER(curses.h, [], [
have_curses="no"
])
-AC_CHECK_LIB([curses], [initscr], [], [
- have_curses="no"
-])
+gui_ldflags="$curses_libs"
+AC_CHECK_LIB([ncursesw], [initscr],
+ [gui_ldflags="$curses_libs -lncursesw"], [
+ AC_CHECK_LIB([curses], [initscr],
+ [gui_ldflags="$curses_libs -lcurses"],
+ [have_curses="no"]
+ )
+ ]
+)
if test "$have_curses" = "yes"; then
AC_SUBST(curses_cppflags)
- AC_DEFINE(HAVE_NCURSES, 1, [define to 1 to turn on curses support])
extras="$extras gui"
executables="$executables gui"
else
default_writer="OSX_WRITE"
AC_DEFINE(HAVE_CORE_AUDIO, 1, define to 1 on Mac Os X)
fi
-########################################################### ogg/vorbis/speex
+####################################################### ogg/vorbis/speex/opus
have_ogg="yes"
OLD_CPPFLAGS="$CPPFLAGS"
OLD_LD_FLAGS="$LDFLAGS"
[look for speex headers also in dir])])
AC_ARG_WITH(speex_libs, [AS_HELP_STRING(--with-speex-libs=dir,
[look for speex libs also in dir])])
+AC_ARG_WITH(opus_headers, [AS_HELP_STRING(--with-opus-headers=dir,
+ [look for opus headers also in dir])])
+AC_ARG_WITH(opus_libs, [AS_HELP_STRING(--with-opus-libs=dir,
+ [look for opus libs also in dir])])
if test -n "$with_ogg_headers"; then
ogg_cppflags="-I$with_ogg_headers"
have_vorbis="yes"
have_speex="yes"
+have_opus="yes"
if test "$have_ogg" = "yes"; then
# vorbis
if test -n "$with_vorbis_headers"; then
fi
AC_CHECK_LIB([speex], [speex_decoder_init], [], [ have_speex="no" ])
AC_CHECK_HEADERS([speex/speex.h], [], [ have_speex="no" ])
+
+ # opus
+ if test -n "$with_opus_headers"; then
+ opus_cppflags="-I$with_opus_headers"
+ CPPFLAGS="$CPPFLAGS $opus_cppflags"
+ fi
+ if test -n "$with_opus_libs"; then
+ speex_libs="-L$with_opus_libs"
+ LDFLAGS="$LDFLAGS $opus_libs"
+ fi
+ AC_CHECK_LIB([opus], [opus_multistream_decode], [], [ have_opus="no" ])
+ AC_CHECK_HEADERS([opus/opus.h], [], [ have_opus="no" ])
else
- AC_MSG_WARN([vorbis/speex depend on libogg, which was not detected])
+ AC_MSG_WARN([vorbis/speex/opus depend on libogg, which was not detected])
have_vorbis="no"
have_speex="no"
+ have_opus="no"
fi
msg="support in para_server/para_filter/para_afh"
-if test "$have_vorbis" = "yes" || test "$have_speex" = "yes"; then
+if test "$have_vorbis" = "yes" || \
+ test "$have_speex" = "yes" || \
+ test "$have_opus" = "yes"; then
AC_SUBST(ogg_cppflags)
ogg_libs="$ogg_libs -logg"
if test "$OSTYPE" = "Darwin"; then
recv_errlist_objs="$recv_errlist_objs ogg_afh"
audiod_audio_formats="$audiod_audio_formats ogg"
- server_audio_formats="$server_audio_formats ogg"
+ audio_format_handlers="$audio_format_handlers ogg"
else
AC_MSG_WARN([no ogg/vorbis $msg])
fi
recv_errlist_objs="$recv_errlist_objs spx_afh spx_common"
audiod_audio_formats="$audiod_audio_formats spx"
- server_audio_formats="$server_audio_formats spx"
+ audio_format_handlers="$audio_format_handlers spx"
else
AC_MSG_WARN([no ogg/speex $msg])
fi
+if test "$have_opus" = "yes"; then
+ all_errlist_objs="$all_errlist_objs opusdec_filter opus_afh opus_common"
+ AC_DEFINE(HAVE_OPUS, 1, define to 1 to turn on ogg/opus support)
+ filters="$filters opusdec"
+ opus_libs="-lopus"
+ server_ldflags="$server_ldflags $opus_libs"
+ filter_ldflags="$filter_ldflags $opus_libs"
+ audiod_ldflags="$audiod_ldflags $opus_libs"
+ afh_ldflags="$afh_ldflags $opus_libs"
+ play_ldflags="$play_ldflags $opus_libs"
+ recv_ldflags="$recv_ldflags $opus_libs"
+
+ server_errlist_objs="$server_errlist_objs opus_afh opus_common"
+ filter_errlist_objs="$filter_errlist_objs opusdec_filter opus_common"
+ audiod_errlist_objs="$audiod_errlist_objs opusdec_filter opus_common"
+ afh_errlist_objs="$afh_errlist_objs opus_afh opus_common"
+ play_errlist_objs="$play_errlist_objs opusdec_filter opus_afh opus_common"
+ recv_errlist_objs="$recv_errlist_objs opus_afh opus_common"
+
+ audiod_audio_formats="$audiod_audio_formats opus"
+ server_audio_formats="$server_audio_formats opus"
+else
+ AC_MSG_WARN([no ogg/opus $msg])
+fi
CPPFLAGS="$OLD_CPPFLAGS"
LDFLAGS="$OLD_LDFLAGS"
LIBS="$OLD_LIBS"
recv_ldflags="$afh_ldflags $faad_libs -lfaad"
audiod_audio_formats="$audiod_audio_formats aac"
- server_audio_formats="$server_audio_formats aac"
+ audio_format_handlers="$audio_format_handlers aac"
filters="$filters aacdec"
AC_SUBST(faad_cppflags)
else
afh_ldflags="$afh_ldflags $flac_libs -lFLAC"
recv_ldflags="$afh_ldflags $flac_libs -lFLAC"
filters="$filters flacdec"
- server_audio_formats="$server_audio_formats flac"
+ audio_format_handlers="$audio_format_handlers flac"
audiod_audio_formats="$audiod_audio_formats flac"
AC_SUBST(flac_cppflags)
else
extras="$extras fade"
executables="$executables fade"
all_errlist_objs="$all_errlist_objs fade"
- fade_errlist_objs="$fade_errlist_objs fade exec string fd"
+ fade_errlist_objs="$fade_errlist_objs fade exec string fd version ggo"
fade_cmdline_objs="add_cmdline(fade)"
fade_objs="$fade_cmdline_objs $fade_errlist_objs"
AC_SUBST(fade_objs, add_dot_o($fade_objs))
AC_DEFINE_UNQUOTED(STATUS_ITEM_ARRAY, [$result],
[char * array of all status items])
-AC_DEFINE_UNQUOTED(SERVER_AUDIO_FORMATS, "$server_audio_formats",
+AC_DEFINE_UNQUOTED(AUDIO_FORMAT_HANDLERS, "$audio_format_handlers",
[formats supported by para_server and para_afh])
AC_SUBST(executables, add_para($executables))
objlist_to_errlist($audioc_errlist_objs), errors used by para_audioc)
AC_SUBST(gui_objs, add_dot_o($gui_objs))
+AC_SUBST(gui_ldflags, $gui_ldflags)
AC_DEFINE_UNQUOTED(INIT_GUI_ERRLISTS,
objlist_to_errlist($gui_errlist_objs), errors used by para_gui)
AC_DEFINE_UNQUOTED(INIT_PLAY_ERRLISTS,
objlist_to_errlist($play_errlist_objs), errors used by para_play)
-AC_MSG_NOTICE(play objs: $play_objs)
-
enum="$(for i in $filters; do printf "${i}_FILTER, " | tr '[a-z]' '[A-Z]'; done)"
AC_DEFINE_UNQUOTED(FILTER_ENUM, $enum NUM_SUPPORTED_FILTERS,
enum of supported filters)
~~~~~~~~~~~~~~~~~~~~~~~~
unix socket credentials: $have_ucred
readline (interactive CLIs): $have_readline
-audio formats supported by para_server/para_afh: $server_audio_formats
+audio formats handlers: $audio_format_handlers
id3 version2 support: $have_libid3tag
-filters supported by para_audiod/para_filter: $filters
-writers supported by para_audiod/para_write: $writers
+filters: $filters
+writers: $writers
optional executables: $extras
$mixer_summary
])
#include <pwd.h>
#include <sys/types.h> /* getgrnam() */
#include <grp.h>
-#include <sys/time.h>
#include <signal.h>
#include "para.h"
fprintf(fp, "%s", color);
if (log_time || log_timing) {
struct timeval tv;
- gettimeofday(&tv, NULL);
+ clock_get_realtime(&tv);
if (daemon_test_flag(DF_LOG_TIME)) { /* print date and time */
time_t t1 = tv.tv_sec;
char str[100];
{
struct dccp_recv_args_info *tmp = para_calloc(sizeof(*tmp));
- if (!dccp_recv_cmdline_parser(argc, argv, tmp) &&
- dccp_recv_ccid_support_check(tmp))
- return tmp;
- free(tmp);
- return NULL;
+ dccp_recv_cmdline_parser(argc, argv, tmp);
+ if (!dccp_recv_ccid_support_check(tmp))
+ exit(EXIT_FAILURE);
+ return tmp;
}
static void dccp_recv_pre_select(struct sched *s, struct task *t)
para_fd_set(rn->fd, &s->rfds, &s->max_fileno);
}
-static void dccp_recv_post_select(struct sched *s, struct task *t)
+static int dccp_recv_post_select(struct sched *s, struct task *t)
{
struct receiver_node *rn = container_of(t, struct receiver_node, task);
struct btr_node *btrn = rn->btrn;
btr_add_output_pool(rn->btrp, num_bytes - iov[0].iov_len, btrn);
}
out:
- if (ret >= 0)
- return;
- btr_remove_node(&rn->btrn);
- t->error = ret;
+ if (ret < 0)
+ btr_remove_node(&rn->btrn);
+ return ret;
}
static void dccp_recv_free_config(void *conf)
r->post_select = dccp_recv_post_select;
r->parse_config = dccp_recv_parse_config;
r->free_config = dccp_recv_free_config;
- r->help = (struct ggo_help) {
- .short_help = dccp_recv_args_info_help,
- .detailed_help = dccp_recv_args_info_detailed_help
- };
+ r->help = (struct ggo_help)DEFINE_GGO_HELP(dccp_recv);
dccp_recv_cmdline_parser_free(&dummy);
}
#define STDIN_ERRORS
#define WRITE_ERRORS
#define CHECK_WAV_ERRORS
+#define VERSION_ERRORS
+#define SCHED_ERRORS
extern const char **para_errlist[];
#define OSS_MIX_ERRORS \
PARA_ERROR(OSS_MIXER_CHANNEL, "invalid mixer channel"), \
+
#define ALSA_MIX_ERRORS \
PARA_ERROR(ALSA_MIX_OPEN, "could not open mixer"), \
PARA_ERROR(ALSA_MIX_BAD_ELEM, "invalid/unsupported control element"), \
PARA_ERROR(LIBSAMPLERATE, "secret rabbit code error"), \
+#define OPUS_COMMON_ERRORS \
+ PARA_ERROR(OPUS_HEADER, "invalid opus header"), \
+
+
+#define OPUS_AFH_ERRORS \
+ PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \
+
+
+#define OPUSDEC_FILTER_ERRORS \
+ PARA_ERROR(CREATE_OPUS_DECODER, "could not create opus decoder"), \
+ PARA_ERROR(OPUS_SET_GAIN, "opus: could not set gain"), \
+ PARA_ERROR(OPUS_DECODE, "opus decode error"), \
+
#define SIDEBAND_ERRORS \
PARA_ERROR(BAD_BAND, "invalid or unexpected band designator"), \
PARA_ERROR(SB_PACKET_SIZE, "invalid sideband packet size or protocol error"), \
#define PLAY_ERRORS \
PARA_ERROR(PLAY_SYNTAX, "para_play: syntax error"), \
PARA_ERROR(NO_VALID_FILES, "no valid file found in playlist"), \
- PARA_ERROR(TERM_RQ, "user termination request"), \
PARA_ERROR(BAD_PLAY_CMD, "invalid command"), \
#define COMPRESS_FILTER_ERRORS \
- PARA_ERROR(COMPRESS_SYNTAX, "syntax error in compress filter config"), \
PARA_ERROR(COMPRESS_EOF, "compress: end of file"), \
#define AFH_ERRORS \
PARA_ERROR(AFH_SYNTAX, "afh syntax error"), \
- PARA_ERROR(AFH_SHORT_WRITE, "afh short write"), \
#define AFH_COMMON_ERRORS \
#define AUDIOC_ERRORS \
- PARA_ERROR(AUDIOC_SYNTAX, "audioc syntax error"), \
PARA_ERROR(AUDIOC_EOF, "audioc: end of file"), \
PARA_ERROR(INCOMPAT_FEAT, "client/server incompatibility"), \
-#define SCHED_ERRORS \
- PARA_ERROR(NOT_INITIALIZED, "scheduler not yet initialized"), \
- PARA_ERROR(SCHED_SHUTDOWN, "scheduler was shut down"), \
-
-
#define NET_ERRORS \
PARA_ERROR(NAME_TOO_LONG, "name too long for struct sockaddr_un"), \
PARA_ERROR(ADDRESS_LOOKUP, "can not resolve requested address"),\
#define UDP_RECV_ERRORS \
- PARA_ERROR(UDP_SYNTAX, "udp_recv syntax error"), \
PARA_ERROR(UDP_OVERRUN, "output buffer overrun"), \
PARA_ERROR(UNSUPPORTED_AUDIO_FORMAT, "given audio format not supported"), \
PARA_ERROR(NOT_PLAYING, "not playing"), \
PARA_ERROR(AUDIOD_OFF, "audiod switched off"), \
- PARA_ERROR(STATUS_TIMEOUT, "timeout reading server status"), \
#define AUDIOD_COMMAND_ERRORS \
#define MP3DEC_FILTER_ERRORS \
PARA_ERROR(MAD_FRAME_DECODE, "mad frame decode error"), \
- PARA_ERROR(MP3DEC_SYNTAX, "syntax error in mp3dec config"), \
PARA_ERROR(MP3DEC_EOF, "mp3dec: end of file"), \
PARA_ERROR(MP3DEC_CORRUPT, "too many corrupt frames"), \
#define FILTER_ERRORS \
PARA_ERROR(NO_FILTERS, "at least one filter must be given"), \
- PARA_ERROR(FILTER_SYNTAX, "syntax error"), \
#define STRING_ERRORS \
PARA_ERROR(SEXP_BUILD, "could not build S-expression"), \
PARA_ERROR(SEXP_ENCRYPT, "could not encrypt S-expression"), \
PARA_ERROR(SEXP_DECRYPT, "could not decrypt S-expression"), \
- PARA_ERROR(MD_OPEN, "could not open message digest object"), \
- PARA_ERROR(CIPHER_OPEN, "could not create stream cipher handle"), \
PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
PARA_ERROR(KEY_MARKER, "invalid/missing key header or footer"), \
PARA_ERROR(ASN1_PARSE, "could not parse ASN.1 key"), \
#include "string.h"
#include "mix.h"
#include "error.h"
+#include "ggo.h"
#include "version.h"
INIT_FADE_ERRLISTS;
exit(EXIT_FAILURE);
}
+__noreturn static void print_help_and_die(void)
+{
+ struct ggo_help h = DEFINE_GGO_HELP(fade);
+ bool d = conf.detailed_help_given;
+
+ ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+ exit(0);
+}
+
int main(int argc, char *argv[])
{
int ret;
struct mixer *m;
struct mixer_handle *h = NULL;
- if (fade_cmdline_parser(argc, argv, &conf))
- exit(EXIT_FAILURE);
- HANDLE_VERSION_FLAG("fade", conf);
+ fade_cmdline_parser(argc, argv, &conf);
+ loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ version_handle_flag("fade", conf.version_given);
+ if (conf.help_given || conf.detailed_help_given)
+ print_help_and_die();
ret = configfile_exists();
if (!ret && conf.config_file_given) {
PARA_EMERG_LOG("can not read config file %s\n",
};
fade_cmdline_parser_config_file(conf.config_file_arg,
&conf, ¶ms);
+ loglevel = get_loglevel_by_name(conf.loglevel_arg);
}
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
init_mixers();
m = get_mixer_or_die();
ret = m->open(conf.mixer_device_arg, &h);
fn->private_data = NULL;
}
-static void fecdec_post_select(__a_unused struct sched *s, struct task *t)
+static int fecdec_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct btr_node *btrn = fn->btrn;
btr_consume(btrn, FEC_HEADER_SIZE + h.slice_bytes);
goto next_buffer;
out:
- t->error = ret;
if (ret < 0)
btr_remove_node(&fn->btrn);
+ return ret;
}
static void fecdec_open(struct filter_node *fn)
#include <regex.h>
#include <sys/types.h>
-#include <sys/time.h>
#include "para.h"
#include "list.h"
__must_check __malloc static char *random_filename(void)
{
char *result, *home = para_homedir();
- struct timeval tv;
- gettimeofday(&tv, NULL);
- srandom(tv.tv_usec);
+ srandom(clock_get_realtime(NULL)->tv_usec);
result = make_message("%s/.paraslash/%08lu", home,
para_random(99999999));
free(home);
free(pfwd);
}
-static void file_write_post_select(__a_unused struct sched *s,
+static int file_write_post_select(__a_unused struct sched *s,
struct task *t)
{
struct writer_node *wn = container_of(t, struct writer_node, task);
goto out;
}
if (!FD_ISSET(pfwd->fd, &s->wfds))
- return;
+ return 0;
bytes = btr_next_buffer(btrn, &buf);
assert(bytes > 0);
//PARA_INFO_LOG("writing %zu\n", bytes);
- ret = write(pfwd->fd, buf, bytes);
+ ret = xwrite(pfwd->fd, buf, bytes);
if (ret < 0)
goto out;
btr_consume(btrn, ret);
out:
if (ret < 0)
btr_remove_node(&wn->btrn);
- t->error = ret;
+ return ret;
}
__malloc static void *file_write_parse_config_or_die(int argc, char **argv)
w->parse_config_or_die = file_write_parse_config_or_die;
w->free_config = file_write_free_config;
w->close = file_write_close;
- w->help = (struct ggo_help) {
- .short_help = file_write_args_info_help,
- .detailed_help = file_write_args_info_detailed_help
- };
+ w->help = (struct ggo_help)DEFINE_GGO_HELP(file_write);
file_write_cmdline_parser_free(&dummy);
}
__noreturn static void print_help_and_die(void)
{
- int d = conf.detailed_help_given;
- const char **p = d? filter_args_info_detailed_help
- : filter_args_info_help;
-
- printf_or_die("%s\n\n", FILTER_CMDLINE_PARSER_PACKAGE "-"
- FILTER_CMDLINE_PARSER_VERSION);
- printf_or_die("%s\n\n", filter_args_info_usage);
- for (; *p; p++)
- printf_or_die("%s\n", *p);
- print_filter_helps(d);
+ struct ggo_help h = DEFINE_GGO_HELP(filter);
+ bool d = conf.detailed_help_given;
+
+ ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+ print_filter_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
exit(0);
}
-static int parse_config(int argc, char *argv[])
+static int parse_config(void)
{
static char *cf; /* config file */
struct stat statbuf;
- if (filter_cmdline_parser(argc, argv, &conf))
- return -E_FILTER_SYNTAX;
- HANDLE_VERSION_FLAG("filter", conf);
+ version_handle_flag("filter", conf.version_given);
if (conf.help_given || conf.detailed_help_given)
print_help_and_die();
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
if (!cf) {
char *home = para_homedir();
cf = make_message("%s/.paraslash/filter.conf", home);
.check_ambiguity = 0,
.print_errors = 1
};
- if (filter_cmdline_parser_config_file(cf, &conf, ¶ms))
- return -E_FILTER_SYNTAX;
+ filter_cmdline_parser_config_file(cf, &conf, ¶ms);
+ loglevel = get_loglevel_by_name(conf.loglevel_arg);
}
if (!conf.filter_given)
return -E_NO_FILTERS;
struct btr_node *parent;
struct filter_node **fns;
+ filter_cmdline_parser(argc, argv, &conf); /* aborts on errors */
+ loglevel = get_loglevel_by_name(conf.loglevel_arg);
filter_init();
- ret = parse_config(argc, argv);
+ ret = parse_config();
if (ret < 0)
goto out;
sit->btrn = btr_new_node(&(struct btr_node_description)
* Convert (filter) the given data.
*
* Pointer to the converting function of the filter. On errors, the
- * post_select function is supposed to set t->error to a (negative)
- * error code.
+ * post_select function is supposed to return a negative error code.
*/
- void (*post_select)(struct sched *s, struct task *t);
+ int (*post_select)(struct sched *s, struct task *t);
/**
* Answer a buffer tree query.
*
void filter_init(void);
int check_filter_arg(char *filter_arg, void **conf);
-void print_filter_helps(int detailed);
+void print_filter_helps(unsigned flags);
void generic_filter_pre_select(struct sched *s, struct task *t);
int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
char **result);
/**
* Print help text of each filter to stdout.
*
- * \param detailed If non-zero, print detailed help.
+ * \param flags Passed to \ref ggo_print_help().
*/
-void print_filter_helps(int detailed)
+void print_filter_helps(unsigned flags)
{
- int i;
+ int i, num = 0;
- printf_or_die("\nAvailable filters: \n\t");
- FOR_EACH_SUPPORTED_FILTER(i)
- printf_or_die("%s%s", i? " " : "", filters[i].name);
- printf_or_die("\n\n");
+ printf_or_die("\nAvailable filters: ");
+ FOR_EACH_SUPPORTED_FILTER(i) {
+ if (num > 50) {
+ printf_or_die("\n ");
+ num = 0;
+ }
+ num += printf_or_die("%s%s", i? " " : "", filters[i].name);
+ }
+ printf_or_die("\n");
FOR_EACH_SUPPORTED_FILTER(i) {
struct filter *f = filters + i;
if (!f->help.short_help)
continue;
- printf_or_die("Options for %s:\n", f->name);
- ggo_print_help(&f->help, detailed);
+ printf_or_die("\nOptions for %s (%s):", f->name,
+ f->help.purpose);
+ ggo_print_help(&f->help, flags);
}
}
/**
- * Set select timeout of the the scheduler.
+ * Set select timeout of the scheduler.
*
* \param s The scheduler.
* \param t The task struct of this filter.
*
* This looks at the status of the btr node of the filter. If data is available
- * in the input queue of the filter, or if an error occured, a minimal timeout
+ * in the input queue of the filter, or if an error occurred, a minimal timeout
* for the next select call is requested from the scheduler. Otherwise the
* scheduler timeout is left unchanged.
*/
return sched_min_delay(s);
}
-static void flacdec_post_select(__a_unused struct sched *s, struct task *t)
+static int flacdec_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct private_flacdec_data *pfd = fn->private_data;
int ret;
if (output_queue_full(btrn))
- return;
+ return 0;
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (ret < 0 && ret != -E_BTR_EOF) /* fatal error */
goto out;
fn->min_iqs = 0;
ret = 1;
out:
- t->error = ret;
if (ret < 0)
btr_remove_node(&fn->btrn);
+ return ret;
}
static void flacdec_close(struct filter_node *fn)
PARA_DEBUG_LOG("decrypted buffer before unpad (%d bytes):\n",
key_size);
- dump_buffer("non-unpadded decrypted buffer", oaep_buf, key_size);;
+ dump_buffer("non-unpadded decrypted buffer", oaep_buf, key_size);
ret = unpad_oaep(oaep_buf, key_size, outbuf, nbytes);
if (ret < 0)
goto out_mpi_release;
PARA_DEBUG_LOG("decrypted buffer after unpad (%zu bytes):\n",
*nbytes);
- dump_buffer("unpadded decrypted buffer", outbuf, *nbytes);;
+ dump_buffer("unpadded decrypted buffer", outbuf, *nbytes);
ret = 1;
out_mpi_release:
gcry_mpi_release(out_mpi);
#include "para.h"
#include "ggo.h"
+#include "version.h"
/**
* Wrapper for printf() that exits on errors.
*
* \param fmt Usual format string.
+ *
+ * \return The return value of the underlying (successful) call to vprintf(3),
+ * i.e. the number of characters printed, excluding the terminating null byte.
*/
-__printf_1_2 void printf_or_die(const char *fmt, ...)
+__printf_1_2 int printf_or_die(const char *fmt, ...)
{
va_list argp;
int ret;
ret = vprintf(fmt, argp);
va_end(argp);
if (ret >= 0)
- return;
+ return ret;
exit(EXIT_FAILURE);
}
* Print one of the two given help texts.
*
* \param help contains the help texts.
- * \param detailed_help Whether to print the detailed help text.
+ * \param flags What to print, see \ref ggo_print_help_flags.
*/
-void ggo_print_help(struct ggo_help *help, int detailed_help)
+void ggo_print_help(struct ggo_help *help, unsigned flags)
{
const char **p;
- if (!help)
- return;
- if (detailed_help)
+ if (help->purpose && (flags & GPH_PRINT_NAME_PURPOSE))
+ printf_or_die("para_%s - %s\n", help->prefix, help->purpose);
+ if (help->usage && (flags & GPH_PRINT_USAGE))
+ printf_or_die("\n%s\n", help->usage);
+ if (help->description && (flags & GPH_PRINT_DESCRIPTION))
+ printf_or_die("\n%s\n", help->description);
+ printf_or_die("\n");
+ if (flags & GPH_DETAILED)
p = help->detailed_help;
else
p = help->short_help;
if (!p)
return;
- p += 3; /* skip -h and -V */
for (; *p; p++)
- printf_or_die("\t%s\n", *p);
+ printf_or_die("%s\n", *p);
}
/** \file ggo.h Functions and structures for help text handling. */
/**
- * Used by executables that can not use gengetopt's generated help function.
+ * Information extracted from the .cmdline.h header files.
*/
struct ggo_help {
- /** The lines of the short help text. */
+ /** Program or module (receiver, filter, writer) name. */
+ const char *prefix;
+ /** Generated by gengetopt from the options in the .ggo file. */
const char **short_help;
- /** The lines of the detailed help text. */
+ /** Like \a short_help, plus the \a details section. */
const char **detailed_help;
+ /** The purpose text as specified in the ggo file. */
+ const char *purpose;
+ /** Generated by gengetopt and exported via the *.cmdline.h file. */
+ const char *usage;
+ /** The description text given in the .ggo file. */
+ const char *description;
};
-void ggo_print_help(struct ggo_help *help, int detailed_help);
-__printf_1_2 void printf_or_die(const char *fmt, ...);
+/**
+ * Control the output of \ref ggo_print_help().
+ *
+ * Any combination of these flags may be passed to ggo_print_help().
+ * Note that the list of supported options is always printed.
+ */
+enum ggo_print_help_flags {
+ /** Whether to print the short help or the detailed help. */
+ GPH_DETAILED = 1 << 0,
+ /** Print the program or module name and the purpose text. */
+ GPH_PRINT_NAME_PURPOSE = 1 << 1,
+ /** Print the synopsis. */
+ GPH_PRINT_USAGE = 1 << 2,
+ /** Print the description text. */
+ GPH_PRINT_DESCRIPTION = 1 << 3,
+};
+
+/** Used to print the normal help of programs (--help) */
+#define GPH_STANDARD_FLAGS \
+ (GPH_PRINT_NAME_PURPOSE | GPH_PRINT_USAGE)
+
+/** Additional information for --detailed-help. */
+#define GPH_STANDARD_FLAGS_DETAILED \
+ (GPH_STANDARD_FLAGS | GPH_DETAILED | GPH_PRINT_DESCRIPTION)
+
+/** For module help embedded in a program help. */
+#define GPH_MODULE_FLAGS 0
+
+/** Modules help with detailed descriptions. */
+#define GPH_MODULE_FLAGS_DETAILED GPH_DETAILED | GPH_PRINT_DESCRIPTION
+
+/** Make a ggo_help structure using information from the .cmdline.h file. */
+#define DEFINE_GGO_HELP(_prefix) \
+ { \
+ .prefix = #_prefix, \
+ .short_help = _prefix ## _args_info_help, \
+ .detailed_help = _prefix ## _args_info_detailed_help, \
+ .purpose = _prefix ## _args_info_purpose, \
+ .usage = _prefix ## _args_info_usage, \
+ .description = _prefix ## _args_info_description, \
+ }
+
+void ggo_print_help(struct ggo_help *help, unsigned flags);
+__printf_1_2 int printf_or_die(const char *fmt, ...);
* We need this forward declaration as post_select() needs
* activate_grab_client and vice versa.
*/
-static void gc_post_select(struct sched *s, struct task *t);
+static int gc_post_select(struct sched *s, struct task *t);
/**
* Move a grab client to the active list and start it.
return 0;
}
-static void gc_post_select(__a_unused struct sched *s, struct task *t)
+static int gc_post_select(__a_unused struct sched *s, struct task *t)
{
struct grab_client *gc = container_of(t, struct grab_client, task);
struct btr_node *btrn = gc->btrn;
size_t sz;
char *buf;
- t->error = 0;
ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
if (ret == 0)
- return;
+ return 0;
if (ret < 0)
goto err;
sz = btr_next_buffer(btrn, &buf);
goto err;
if (ret > 0)
btr_consume(btrn, ret);
- return;
+ return 0;
err:
gc_close(gc, ret);
- t->error = ret;
+ return ret;
}
static int gc_check_args(int argc, char **argv, struct grab_client *gc)
#include <signal.h>
#include <sys/types.h>
#include <curses.h>
+#include <locale.h>
+#include <sys/time.h>
#include "gui.cmdline.h"
#include "para.h"
#include "list.h"
#include "sched.h"
#include "signal.h"
+#include "ggo.h"
#include "version.h"
/** define the array of error lists needed by para_gui */
static unsigned scroll_position;
-static int cmd_died, curses_active;
+static int curses_active;
static pid_t cmd_pid;
static int command_fds[2];
return file_exists(tmp)? tmp: NULL;
}
-/*
- * print num spaces to curses window
- */
+/* Print given number of spaces to curses window. */
static void add_spaces(WINDOW* win, unsigned int num)
{
- while (num > 0) {
- num--;
- waddstr(win, " ");
+ char space[] = " ";
+ unsigned sz = sizeof(space) - 1; /* number of spaces */
+
+ while (num >= sz) {
+ waddstr(win, space);
+ num -= sz;
+ }
+ if (num > 0) {
+ assert(num < sz);
+ space[num] = '\0';
+ waddstr(win, space);
}
}
static int align_str(WINDOW* win, char *str, unsigned int len,
unsigned int align)
{
- int i, num; /* of spaces */
+ int ret, i, num; /* of spaces */
+ size_t width;
if (!win || !str)
- return -1;
- num = len - strlen(str);
+ return 0;
+ ret = strwidth(str, &width);
+ if (ret < 0) {
+ PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ width = 0;
+ str[0] = '\0';
+ }
+ num = len - width;
if (num < 0) {
str[len] = '\0';
num = 0;
return RINGBUFFER_SIZE - 1;
}
+/*
+returns number of first visible rbe, *lines is the number of lines drawn.
+ */
static int draw_top_rbe(unsigned *lines)
{
- unsigned len;
- int offset, fvr = first_visible_rbe(lines);
+ int ret, fvr = first_visible_rbe(lines);
struct rb_entry *rbe;
+ size_t bytes_to_skip, cells_to_skip, width;
if (fvr < 0)
return -1;
rbe = ringbuffer_get(bot_win_rb, fvr);
if (!rbe)
return -1;
- len = strlen(rbe->msg);
if (*lines > bot.lines) {
- /* first rbe is only partially visible */
- offset = (*lines - bot.lines) * bot.cols;
- assert(offset <= len);
- } else
- offset = 0;
+ /* rbe is partially visible multi-line */
+ cells_to_skip = (*lines - bot.lines) * bot.cols;
+ ret = skip_cells(rbe->msg, cells_to_skip, &bytes_to_skip);
+ if (ret < 0)
+ return ret;
+ ret = strwidth(rbe->msg + bytes_to_skip, &width);
+ if (ret < 0)
+ return ret;
+ } else {
+ bytes_to_skip = 0;
+ width = rbe->len;
+ }
wattron(bot.win, COLOR_PAIR(rbe->color));
- waddstr(bot.win, rbe->msg + offset);
- *lines = NUM_LINES(len - offset);
+ waddstr(bot.win, rbe->msg + bytes_to_skip);
+ *lines = NUM_LINES(width);
return fvr;
}
static void rb_add_entry(int color, char *msg)
{
- struct rb_entry *old, *new = para_malloc(sizeof(struct rb_entry));
+ struct rb_entry *old, *new;
int x, y;
+ size_t len;
+
+ if (strwidth(msg, &len) < 0)
+ return;
+ new = para_malloc(sizeof(struct rb_entry));
new->color = color;
- new->len = strlen(msg);
+ new->len = len;
new->msg = msg;
old = ringbuffer_add(bot_win_rb, new);
// fprintf(stderr, "added: %s\n", new->msg);
para_sigaction(SIGHUP, SIG_IGN);
}
-__noreturn static void do_exit(int ret)
+/* kill every process in the process group and exit */
+__noreturn static void kill_pg_and_die(int ret)
{
para_sigaction(SIGTERM, SIG_IGN);
kill(0, SIGTERM);
__noreturn static void finish(int ret)
{
shutdown_curses();
- do_exit(ret);
+ kill_pg_and_die(ret);
}
/*
va_start(argp, fmt);
vfprintf(outfd, fmt, argp);
va_end(argp);
- do_exit(ret);
+ kill_pg_and_die(ret);
}
static void print_welcome(void)
{
if (loglevel > LL_NOTICE)
return;
- outputf(COLOR_WELCOME, "Welcome to para_gui " PACKAGE_VERSION
- " \"" CODENAME "\". Theme: %s", theme.name);
+ outputf(COLOR_WELCOME, "Welcome to %s. Theme: %s",
+ version_single_line("gui"), theme.name);
wclrtoeol(bot.win);
}
ret = para_reap_child(&pid);
if (ret <= 0)
return;
- if (pid == cmd_pid) {
+ if (pid == cmd_pid)
cmd_pid = 0;
- cmd_died = 1;
- }
goto reap_next_child;
}
}
}
-static int open_stat_pipe(void)
+static void status_pre_select(fd_set *rfds, int *max_fileno, struct timeval *tv)
{
- static int init = 1;
+ static struct timeval next_exec, atm, diff;
int ret, fds[3] = {0, 1, 0};
pid_t pid;
- if (init)
- init = 0;
- else
- /*
- * Sleep a bit to avoid a busy loop. As the call to sleep() may
- * be interrupted by SIGCHLD, we simply wait until the call
- * succeeds.
- */
- while (sleep(2))
- ; /* nothing */
+ if (stat_pipe >= 0)
+ goto success;
+ /* Avoid busy loop */
+ gettimeofday(&atm, NULL);
+ if (tv_diff(&next_exec, &atm, &diff) > 0) {
+ if (tv_diff(&diff, tv, NULL) < 0)
+ *tv = diff;
+ return;
+ }
+ next_exec.tv_sec = atm.tv_sec + 2;
ret = para_exec_cmdline_pid(&pid, conf.stat_cmd_arg, fds);
if (ret < 0)
- return ret;
+ return;
ret = mark_fd_nonblocking(fds[1]);
- if (ret >= 0)
- return fds[1];
- close(fds[1]);
- return ret;
+ if (ret < 0) {
+ close(fds[1]);
+ return;
+ }
+ stat_pipe = fds[1];
+success:
+ para_fd_set(stat_pipe, rfds, max_fileno);
}
-#define COMMAND_BUF_SIZE 4096
+#define COMMAND_BUF_SIZE 32768
/*
* This is the core select loop. Besides the (internal) signal
char command_buf[2][COMMAND_BUF_SIZE] = {"", ""};
int cbo[2] = {0, 0}; /* command buf offsets */
struct timeval tv;
+ unsigned flags[2] = {0, 0}; /* for for_each_line() */
repeat:
tv.tv_sec = conf.timeout_arg / 1000;
// ret = refresh_status();
FD_ZERO(&rfds);
max_fileno = 0;
- if (stat_pipe < 0)
- stat_pipe = open_stat_pipe();
- if (stat_pipe >= 0)
- para_fd_set(stat_pipe, &rfds, &max_fileno);
+ status_pre_select(&rfds, &max_fileno, &tv);
/* signal pipe */
para_fd_set(signal_pipe, &rfds, &max_fileno);
/* command pipe only for COMMAND_MODE */
if (command_fds[1] >= 0)
para_fd_set(command_fds[1], &rfds, &max_fileno);
}
+ if (mode == GETCH_MODE || mode == COMMAND_MODE)
+ para_fd_set(STDIN_FILENO, &rfds, &max_fileno);
ret = para_select(max_fileno + 1, &rfds, NULL, &tv);
if (ret <= 0)
goto check_return; /* skip fd checks */
COMMAND_BUF_SIZE - 1 - cbo[i], &rfds, &sz);
cbo[i] += sz;
sz = cbo[i];
- cbo[i] = for_each_line(command_buf[i], cbo[i],
+ cbo[i] = for_each_line(flags[i], command_buf[i], cbo[i],
add_output_line, &i);
- if (sz != cbo[i])
+ if (sz != cbo[i]) { /* at least one line found */
wrefresh(bot.win);
+ flags[i] = 0;
+ }
if (ret < 0) {
PARA_NOTICE_LOG("closing command fd %d: %s",
i, para_strerror(-ret));
close(command_fds[i]);
command_fds[i] = -1;
+ flags[i] = 0;
if (command_fds[!i] < 0) /* both fds closed */
return 0;
}
+ if (cbo[i] == COMMAND_BUF_SIZE - 1) {
+ PARA_NOTICE_LOG("discarding overlong line");
+ cbo[i] = 0;
+ flags[i] = FELF_DISCARD_FIRST;
+ }
}
}
ret = read_stat_pipe(&rfds);
return ret;
break;
case EXTERNAL_MODE:
- if (cmd_died) {
- cmd_died = 0;
+ if (cmd_pid == 0)
return 0;
- }
}
goto repeat;
}
shutdown_curses();
if (para_exec_cmdline_pid(&cmd_pid, cmd, fds) < 0)
return;
- cmd_died = 0;
do_select(EXTERNAL_MODE);
init_curses();
}
struct rb_entry *rbe = ringbuffer_get(bot_win_rb, i);
if (!rbe)
break;
- lines += NUM_LINES(strlen(rbe->msg));
+ lines += NUM_LINES(rbe->len);
}
i++;
if (lines > 0 && scroll_position != i) {
struct rb_entry *rbe = ringbuffer_get(bot_win_rb, i);
if (!rbe)
break;
- lines += NUM_LINES(strlen(rbe->msg));
+ lines += NUM_LINES(rbe->len);
}
if (lines) {
scroll_position = i;
}
PARA_INFO_LOG("rereading command line options and config file");
gui_cmdline_parser_ext(_argc, _argv, &conf, ¶ms);
- if (gui_cmdline_parser_config_file(cf, &conf, ¶ms) != 0) {
- PARA_EMERG_LOG("errors in config file");
- finish(EXIT_FAILURE);
- }
+ gui_cmdline_parser_config_file(cf, &conf, ¶ms);
PARA_NOTICE_LOG("config file reloaded");
if (check_key_map_args() < 0)
finish(EXIT_FAILURE);
static void com_version(void)
{
- print_in_bar(COLOR_MSG, "para_gui " PACKAGE_VERSION " \""
- CODENAME "\"");
+ print_in_bar(COLOR_MSG, "%s", version_single_line("gui"));
}
__noreturn static void com_quit(void)
km_keyname(c));
}
+__noreturn static void print_help_and_die(void)
+{
+ struct ggo_help h = DEFINE_GGO_HELP(gui);
+ bool d = conf.detailed_help_given;
+
+ ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+ exit(0);
+}
+
int main(int argc, char *argv[])
{
int ret;
_argv = argv;
gui_cmdline_parser(argc, argv, &conf); /* exits on errors */
- HANDLE_VERSION_FLAG("gui", conf);
+ loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ version_handle_flag("gui", conf.version_given);
+ if (conf.help_given || conf.detailed_help_given)
+ print_help_and_die();
cf = configfile_exists();
if (!cf && conf.config_file_given) {
fprintf(stderr, "can not read config file %s\n",
.check_ambiguity = 0,
.print_errors = 1,
};
- if (gui_cmdline_parser_config_file(cf, &conf, ¶ms) != 0)
- exit(EXIT_FAILURE);
+ gui_cmdline_parser_config_file(cf, &conf, ¶ms);
+ loglevel = get_loglevel_by_name(conf.loglevel_arg);
}
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
if (check_key_map_args() < 0) {
fprintf(stderr, "invalid key map\n");
exit(EXIT_FAILURE);
top.lines = theme.top_lines_default;
setup_signal_handling();
bot_win_rb = ringbuffer_new(RINGBUFFER_SIZE);
+ setlocale(LC_CTYPE, "");
initscr(); /* needed only once, always successful */
init_curses();
print_welcome();
struct receiver_node *rn = container_of(t, struct receiver_node, task);
struct private_http_recv_data *phd = rn->private_data;
- t->error = 0;
if (generic_recv_pre_select(s, t) <= 0)
return;
if (phd->status == HTTP_CONNECTED)
* area with data read from the socket. In any case, update the state of the
* connection if necessary.
*/
-static void http_recv_post_select(struct sched *s, struct task *t)
+static int http_recv_post_select(struct sched *s, struct task *t)
{
struct receiver_node *rn = container_of(t, struct receiver_node, task);
struct private_http_recv_data *phd = rn->private_data;
if (ret < 0)
goto out;
if (ret == 0)
- return;
+ return 0;
if (phd->status == HTTP_CONNECTED) {
char *rq;
if (!FD_ISSET(rn->fd, &s->wfds))
- return;
+ return 0;
rq = make_request_msg();
PARA_INFO_LOG("sending http request\n");
ret = write_va_buffer(rn->fd, "%s", rq);
if (ret < 0)
goto out;
phd->status = HTTP_SENT_GET_REQUEST;
- return;
+ return 0;
}
if (phd->status == HTTP_SENT_GET_REQUEST) {
ret = read_pattern(rn->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG), &s->rfds);
if (ret < 0)
goto out;
if (ret == 0)
- return;
+ return 0;
PARA_INFO_LOG("received ok msg, streaming\n");
phd->status = HTTP_STREAMING;
- return;
+ return 0;
}
ret = -E_HTTP_RECV_OVERRUN;
iovcnt = btr_pool_get_buffers(rn->btrp, iov);
btr_add_output_pool(rn->btrp, num_bytes - iov[0].iov_len, btrn);
}
out:
- if (ret >= 0)
- return;
- btr_remove_node(&rn->btrn);
- t->error = ret;
+ if (ret < 0)
+ btr_remove_node(&rn->btrn);
+ return ret;
}
static void http_recv_close(struct receiver_node *rn)
{
struct http_recv_args_info *tmp = para_calloc(sizeof(*tmp));
- if (!http_recv_cmdline_parser(argc, argv, tmp))
- return tmp;
- free(tmp);
- return NULL;
+ http_recv_cmdline_parser(argc, argv, tmp);
+ return tmp;
}
static int http_recv_open(struct receiver_node *rn)
r->post_select = http_recv_post_select;
r->parse_config = http_recv_parse_config;
r->free_config = http_recv_free_config;
- r->help = (struct ggo_help) {
- .short_help = http_recv_args_info_help,
- .detailed_help = http_recv_args_info_detailed_help
- };
+ r->help = (struct ggo_help)DEFINE_GGO_HELP(http_recv);
http_recv_cmdline_parser_free(&dummy);
}
rl_redisplay();
wipe_bottom_line(); /* wipe out the prompt */
rl_insert_text(text);
+ free(text);
rl_point = point;
}
static void i9e_line_handler(char *line)
{
int ret;
+ struct btr_node *dummy;
+ if (!line) {
+ i9ep->input_eof = true;
+ return;
+ }
+ if (!*line)
+ goto free_line;
+ rl_set_prompt("");
+ dummy = btr_new_node(&(struct btr_node_description)
+ EMBRACE(.name = "dummy line handler"));
+ i9e_attach_to_stdout(dummy);
ret = i9ep->ici->line_handler(line);
if (ret < 0)
PARA_WARNING_LOG("%s\n", para_strerror(-ret));
- rl_set_prompt("");
- if (line) {
- if (!*line)
- rl_set_prompt(i9ep->ici->prompt);
- else
- add_history(line);
- free(line);
- } else {
- rl_set_prompt("");
- i9ep->input_eof = true;
- }
+ add_history(line);
+ btr_remove_node(&dummy);
+free_line:
+ free(line);
}
-static void i9e_post_select(__a_unused struct sched *s, struct task *t)
+static int i9e_post_select(__a_unused struct sched *s, __a_unused struct task *t)
{
int ret;
struct i9e_client_info *ici = i9ep->ici;
wipe_bottom_line();
out:
i9ep->caught_sigint = false;
- t->error = ret;
+ return ret;
}
static void i9e_pre_select(struct sched *s, __a_unused struct task *t)
#endif
if (shmmax == 0) {
PARA_WARNING_LOG("unable to determine shmmax\n");
- shmmax = 65535; /* last ressort */
+ shmmax = 65535; /* last resort */
}
PARA_INFO_LOG("shmmax: %zu\n", shmmax);
return shmmax;
-args "--unamed-opts=audio_file --no-handle-version"
+args "--unamed-opts=audio_file --no-handle-version --no-handle-help"
-include(header.m4)
-<qu>
-text "
-para_afh, the audio format handler tool, is a simple program for analyzing
-audio files. It prints technical information about the given audio file to
-stdout.
-"
-</qu>
+purpose "Print information about audio file(s)."
+include(header.m4)
include(loglevel.m4)
<qu>
-include(header.m4)
-<qu>
-text "
+args "--no-version --no-help"
+
+purpose "Make an audio stream from a local file."
+
+description "
The afh (audio format handler) receiver can be used to write
selected parts of the given audio file without decoding
the data.
The selected parts of the content of the audio file are passed
to the child nodes of the buffer tree. Only complete chunks
with respect of the underlying audio format are passed.
-
"
+include(header.m4)
+<qu>
option "filename" f
#~~~~~~~~~~~~~~~~~~
"file to open"
+args "--no-version --no-help"
+
+purpose "Native ALSA output plugin."
+
include(header.m4)
<qu>
+args "--no-version --no-help"
+
+purpose "Amplify the decoded audio stream."
+
option "amp" a
#~~~~~~~~~~~~~
"amplification value"
+args "--no-version --no-help"
+
+purpose "Output plugin for libao."
+
include(header.m4)
<qu>
-args "--unamed-opts=command --conf-parser --no-handle-version"
+args "--unamed-opts=command --conf-parser --no-handle-version --no-handle-help"
+
+purpose "Communicate with para_audiod through a local socket."
include(header.m4)
<qu>
args "--no-handle-help --no-handle-version --conf-parser"
+purpose "Connect to para_server, receive, decode and play audio streams."
+
include(header.m4)
define(CURRENT_PROGRAM,para_audiod)
define(DEFAULT_CONFIG_FILE,~/.paraslash/audiod.conf)
-args "--unamed-opts=command --no-handle-error --conf-parser --no-handle-version"
+args "--unamed-opts=command --no-handle-error --conf-parser --no-handle-version --no-handle-help"
+
+purpose "Communicate with para_server through the paraslash control port."
include(header.m4)
define(CURRENT_PROGRAM,para_client)
+args "--no-version --no-help"
+
+purpose "Dynamically adjust the volume of an audio stream."
+
option "blocksize" b
#~~~~~~~~~~~~~~~~~~~
"adjust block size"
+args "--no-version --no-help"
+
+purpose "Receive a DCCP audio stream."
+
option "host" i
"ip or host"
string default="localhost"
-args "--conf-parser --no-handle-version"
+args "--conf-parser --no-handle-version --no-handle-help"
+
+purpose "An alarm clock and volume-fader for OSS and ALSA."
include(header.m4)
define(CURRENT_PROGRAM,para_fade)
+args "--no-version --no-help"
+
+purpose "Output plugin that writes to a local file."
+
option "filename" f
#~~~~~~~~~~~~~~~~~~
"specify output file name"
args "--no-handle-help --no-handle-version --conf-parser"
+purpose "Decode or process audio data from STDIN to STDOUT."
+
include(header.m4)
include(loglevel.m4)
<qu>
-args "--conf-parser --no-handle-version"
+args "--conf-parser --no-handle-version --no-handle-help"
+
+purpose "Show para_audiod status in a curses window."
include(header.m4)
define(CURRENT_PROGRAM,para_gui)
+args "--no-version --no-help"
+
+purpose "Receive an HTTP audio stream."
+
include(header.m4)
<qu>
define ggo_opts
--output-dir=$(cmdline_dir) \
- --set-version="$(PACKAGE_VERSION)" \
+ --set-version="$(GIT_VERSION) ($(codename))" \
--arg-struct-name=$(*F)_args_info \
--file-name=$(*F).cmdline \
--func-name=$(*F)_cmdline_parser \
$(cmdline_dir)/%.cmdline.h $(cmdline_dir)/%.cmdline.c: $(ggo_dir)/%.ggo | $(cmdline_dir)
@[ -z "$(Q)" ] || echo 'GGO $<'
$(Q) $(GENGETOPT) $(ggo_opts) < $<
+ifeq ($(ggo_descriptions_declared),no)
+ echo 'extern const char *$(*F)_args_info_description;' >> $(cmdline_dir)/$(*F).cmdline.h
+endif
$(ggo_dir)/server.ggo $(ggo_dir)/audiod.ggo: \
$(m4_ggo_dir)/loglevel.m4 $(m4_ggo_dir)/color.m4 \
+args "--no-version --no-help"
+
+purpose "Decode an mp3 stream."
+
include(header.m4)
<qu>
+args "--no-version --no-help"
+
+purpose "Output plugin for the Open Sound System."
+
option "device" d
#~~~~~~~~~~~~~~~~
"set PCM device"
+args "--no-version --no-help"
+
+purpose "Output plugin for Mac OS coreaudio."
+
section "osx options"
#####################
args "--unamed-opts=audio_file --no-handle-version --conf-parser --no-handle-help"
+
+purpose "Command line audio player."
+
+description "para_play operates either in command mode or in insert
+mode. In insert mode it presents a prompt and allows to enter commands
+like stop, play, pause etc. In command mode the current audio file
+is shown and the program reads single key strokes from stdin. Keys
+may be mapped to commands. Whenever a mapped key is pressed, the
+associated command is executed."
+
include(header.m4)
define(CURRENT_PROGRAM,para_play)
define(DEFAULT_CONFIG_FILE,~/.paraslash/play.conf)
+args "--no-version --no-help"
+
+purpose "Delay processing of an audio stream."
+
option "duration" d
#~~~~~~~~~~~~~~~~~~
"prebuffer time"
args "--no-handle-help --no-handle-version"
+purpose "A command line HTTP/DCCP/UDP stream grabber."
+
include(header.m4)
include(loglevel.m4)
+args "--no-version --no-help"
+
+purpose "Transform raw audio to a different sample rate."
+
include(header.m4)
option "converter" C
-args "--conf-parser --no-handle-version"
+args "--conf-parser --no-handle-version --no-handle-help"
+
+purpose "Manage and stream audio files."
include(header.m4)
define(CURRENT_PROGRAM,para_server)
option "udp_header_interval" H
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"duration for sending header"
-int typestr="ms"
-default="2000"
+int typestr = "ms"
+default = "2000"
optional
-details="
+details = "
As the udp sender has no idea about connected clients it
sends the audio file header periodically if necessary. This
- option is used to specify the duration of the interval between
- sending the header. Shorter values decrease the average time
- clients have to wait before being able to start playback,
- but this also increases the amount network traffic. Note
- that this affects only ogg vorbis streams as this is the only
- audio format that needs an audio file header.
+ option specifies the duration between subsequent headers are
+ sent. Smaller values decrease the average time clients have
+ to wait before starting playback, larger values decrease
+ network traffic.
+
+ Note that this affects only ogg/* and wma streams. Other
+ audio formats, including mp3, don't need an audio file header.
"
option "udp_ttl" t
+args "--no-version --no-help"
+
+purpose "Receive an UDP audio stream."
+
option "host" i
"ip or host to receive udp packets from"
string default="224.0.1.38"
args "--no-handle-help --no-handle-version"
+purpose "Play wav or raw audio."
+
include(header.m4)
include(loglevel.m4)
+/*
+ * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file mix.h Mixer API (used by para_fade). */
+
/**
* Opaque structure which corresponds to an instance of a mixer.
*
if (!*mood_name)
return -E_DUMMY_ROW;
mlpd.m = alloc_new_mood(mood_name);
- ret = for_each_line_ro(mood_def.data, mood_def.size,
+ ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
parse_mood_line, &mlpd);
osl_close_disk_object(&mood_def);
if (ret < 0) {
ret = para_printf(pb, "checking mood %s...\n", mood_name);
if (ret < 0)
goto out;
- ret = for_each_line_ro(mood_def.data, mood_def.size,
+ ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
parse_mood_line, &mlpd);
if (ret < 0)
para_printf(pb, "%s line %u: %s\n", mood_name, mlpd.line_num,
* This function performs actions required due to the occurrence of the given
* event. Possible actions include reload of the current mood and update of the
* score of an audio file.
+ *
+ * \return Standard.
*/
int moods_event_handler(enum afs_events event, __a_unused struct para_buffer *pb,
void *data)
#include <id3tag.h>
-static char *get_latin1(id3_ucs4_t const *string)
+static char *get_utf8(id3_ucs4_t const *string)
{
if (!string)
return NULL;
- return (char *)id3_ucs4_latin1duplicate(string);
+ return (char *)id3_ucs4_utf8duplicate(string);
}
static char *get_stringlist(union id3_field *field)
char *result = NULL;
for (k = 0; k < nstrings; k++) {
- char *tmp = (char *)get_latin1(id3_field_getstrings(field, k));
+ char *tmp = (char *)get_utf8(id3_field_getstrings(field, k));
if (result) {
char *tmp2 = result;
result = make_message("%s %s", tmp2, tmp);
{
id3_ucs4_t const *string = id3_field_getfullstring(field);
- return get_latin1(string);
+ return get_utf8(string);
}
#define FOR_EACH_FIELD(f, j, fr) for (j = 0; j < (fr)->nfields && \
}
for (i = 0; i < id3_t->nframes; i++) {
struct id3_frame *fr = id3_t->frames[i];
- if (!strcmp(fr->id, "TIT2")) {
+ if (!strcmp(fr->id, ID3_FRAME_TITLE)) {
if (!tags->title)
tags->title = get_strings(fr);
continue;
}
- if (!strcmp(fr->id, "TPE1")) {
+ if (!strcmp(fr->id, ID3_FRAME_ARTIST)) {
if (!tags->artist)
tags->artist = get_strings(fr);
continue;
}
- if (!strcmp(fr->id, "TALB")) {
+ if (!strcmp(fr->id, ID3_FRAME_ALBUM)) {
if (!tags->album)
tags->album = get_strings(fr);
continue;
}
- if (!strcmp(fr->id, "TDRC")) {
+ if (!strcmp(fr->id, ID3_FRAME_YEAR)) {
if (!tags->year)
tags->year = get_strings(fr);
continue;
}
- if (!strcmp(fr->id, "COMM")) {
+ if (!strcmp(fr->id, ID3_FRAME_COMMENT)) {
if (!tags->comment)
tags->comment = get_strings(fr);
continue;
#define MP3DEC_MAX_FRAME 8192
-static void mp3dec_post_select(__a_unused struct sched *s, struct task *t)
+static int mp3dec_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
int i, ret;
next_buffer:
pmd->stream.error = 0;
- t->error = 0;
iqs = btr_get_input_queue_size(btrn);
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (ret < 0)
goto err;
if (ret == 0)
- return;
+ return 0;
btr_merge(btrn, fn->min_iqs);
len = btr_next_buffer(btrn, &inbuffer);
/*
}
if (loaded == 0)
goto next_buffer;
- return;
+ return 0;
}
pmd->sample_rate = pmd->frame.header.samplerate;
pmd->channels = MAD_NCHANNELS(&pmd->frame.header);
if (fn->min_iqs > MP3DEC_MAX_FRAME)
goto err;
mp3dec_consume(btrn, &pmd->stream, len);
- return;
+ return 0;
}
if (pmd->stream.error != MAD_ERROR_BADDATAPTR)
goto decode;
mp3dec_consume(btrn, &pmd->stream, len);
- return;
+ return 0;
}
fn->min_iqs = 0;
mad_synth_frame(&pmd->synth, &pmd->frame);
goto next_frame;
err:
assert(ret < 0);
- t->error = ret;
btr_remove_node(&fn->btrn);
+ return ret;
}
static void mp3dec_open(struct filter_node *fn)
f->pre_select = generic_filter_pre_select;
f->post_select = mp3dec_post_select;
f->execute = mp3dec_execute;
- f->help = (struct ggo_help) {
- .short_help = mp3dec_filter_args_info_help,
- .detailed_help = mp3dec_filter_args_info_detailed_help
- };
+ f->help = (struct ggo_help)DEFINE_GGO_HELP(mp3dec_filter);
}
struct list_head sockopts;
};
+/**
+ * Allocate and initialize a flowopt queue.
+ *
+ * \return A new structure to be passed to \ref flowopt_add(). It is
+ * automatically deallocated in \ref makesock().
+ */
struct flowopts *flowopt_new(void)
{
struct flowopts *new = para_malloc(sizeof(*new));
list_add_tail(&new->node, &fo->sockopts);
}
-void flowopt_add_bool(struct flowopts *fo, int lev, int opt,
- const char *optname, bool on_or_off)
-{
- int on = on_or_off; /* kernel takes 'int' */
-
- flowopt_add(fo, lev, opt, optname, &on, sizeof(on));
-}
-
/** Set the entire bunch of pre-connection options at once. */
static void flowopt_setopts(int sockfd, struct flowopts *fo)
{
extern struct flowopts *flowopt_new(void);
extern void flowopt_add(struct flowopts *fo, int level, int opt,
const char *name, const void *val, int len);
-extern void flowopt_add_bool(struct flowopts *fo, int lev, int opt,
- const char *optname, bool on_or_off);
/** Flowopt shortcut macros */
#define OPT_ADD(fo, lev, opt, val, len) flowopt_add(fo, lev, opt, #opt, val, len)
-#define OPT_ENABLE(fo, lev, opt) flowopt_add_bool(fo, lev, opt, #opt, 1)
-#define OPT_DISABLE(fo, lev, opt) flowopt_add_bool(fo, lev, opt, #opt, 0)
/**
* Functions to parse and validate (parts of) URLs.
* Licensed under the GPL v2. For licencing details see COPYING.
*/
-/** \file ogg_afh_common.c Functions common to ogg/vorbis and ogg/speex. */
+/** \file ogg_afh_common.c Functions common to all ogg/ codecs. */
#include <ogg/ogg.h>
#include <regex.h>
sched_min_delay(s);
}
-static void ogg_post_select(__a_unused struct sched *s, struct task *t)
+static int ogg_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct private_oggdec_data *pod = fn->private_data;
} else if (ret == 0 && !pod->have_more) /* nothing to do */
goto out;
if (btr_get_output_queue_size(btrn) > OGGDEC_MAX_OUTPUT_SIZE)
- return;
+ return 0;
if (!pod->vf) {
if (ret <= 0)
goto out;
if (ret == OV_HOLE) /* avoid buffer underruns */
fn->min_iqs = 9000;
if (ret >= 0 || ret == OV_HOLE)
- return;
+ return 0;
ret = -E_OGGDEC_BADLINK;
out:
- t->error = ret;
if (ret < 0)
btr_remove_node(&fn->btrn);
+ return ret;
}
/**
--- /dev/null
+/*
+ * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file opus_afh.c Audio format handler for ogg/opus files. */
+
+#include <ogg/ogg.h>
+#include <regex.h>
+
+#include "para.h"
+#include "afh.h"
+#include "error.h"
+#include "portable_io.h"
+#include "string.h"
+#include "opus_common.h"
+#include "ogg_afh_common.h"
+
+static const char* opus_suffixes[] = {"opus", NULL};
+
+static bool copy_if_tag_type(const char *tag, int taglen, const char *type,
+ char **p)
+{
+ char *q = key_value_copy(tag, taglen, type);
+ if (!q)
+ return false;
+ free(*p);
+ *p = q;
+ return true;
+}
+
+static int opus_get_comments(char *comments, int length,
+ struct taginfo *tags)
+{
+ char *p = comments, *end = comments + length;
+ int i;
+ uint32_t val, ntags;
+
+ /* min size of a opus header is 16 bytes */
+ if (length < 16)
+ return -E_OPUS_COMMENT;
+ if (memcmp(p, "OpusTags", 8) != 0)
+ return -E_OPUS_COMMENT;
+ p += 8;
+ val = read_u32(p);
+ p += 4;
+ if (p + val > end)
+ return -E_OPUS_COMMENT;
+ tags->comment = safe_strdup(p, val);
+ p += val;
+ ntags = read_u32(p);
+ p += 4;
+ if (p + ntags * 4 > end)
+ return -E_OPUS_COMMENT;
+ PARA_INFO_LOG("found %d tag(s)\n", ntags);
+ for (i = 0; i < ntags; i++, p += val) {
+ char *tag;
+
+ if (p + 4 > end)
+ return -E_OPUS_COMMENT;
+ val = read_u32(p);
+ p += 4;
+ if (p + val > end)
+ return -E_OPUS_COMMENT;
+ if (copy_if_tag_type(p, val, "author", &tags->artist))
+ continue;
+ if (copy_if_tag_type(p, val, "artist", &tags->artist))
+ continue;
+ if (copy_if_tag_type(p, val, "title", &tags->title))
+ continue;
+ if (copy_if_tag_type(p, val, "album", &tags->album))
+ continue;
+ if (copy_if_tag_type(p, val, "year", &tags->year))
+ continue;
+ if (copy_if_tag_type(p, val, "comment", &tags->comment))
+ continue;
+ tag = safe_strdup(p, val);
+ PARA_NOTICE_LOG("unrecognized tag: %s\n", tag);
+ free(tag);
+ }
+ return 1;
+}
+
+static int opus_packet_callback(ogg_packet *packet, int packet_num,
+ __a_unused int serial, struct afh_info *afhi,
+ void *private_data)
+{
+ int ret;
+ struct opus_header *oh = private_data;
+
+ if (packet_num == 0) {
+ ret = opus_parse_header((char *)packet->packet, packet->bytes, oh);
+ if (ret < 0)
+ return ret;
+ afhi->channels = oh->channels;
+ afhi->techinfo = make_message("header version %d, input sample rate: %dHz",
+ oh->version, oh->input_sample_rate);
+ /*
+ * The input sample rate is irrelevant for afhi->frequency as
+ * we always decode to 48kHz.
+ */
+ afhi->frequency = 48000;
+ return 1;
+ }
+ if (packet_num == 1) {
+ ret = opus_get_comments((char *)packet->packet, packet->bytes,
+ &afhi->tags);
+ if (ret < 0)
+ return ret;
+ return 0; /* header complete */
+ }
+ /* never reached */
+ assert(0);
+}
+
+static int opus_get_file_info(char *map, size_t numbytes, __a_unused int fd,
+ struct afh_info *afhi)
+{
+ int ret, ms;
+ struct opus_header oh = {.version = 0};
+
+ struct ogg_afh_callback_info opus_callback_info = {
+ .packet_callback = opus_packet_callback,
+ .private_data = &oh,
+ };
+ ret = ogg_get_file_info(map, numbytes, afhi, &opus_callback_info);
+ if (ret < 0)
+ return ret;
+ ret = (afhi->chunk_table[afhi->chunks_total] - afhi->chunk_table[0]) * 8; /* bits */
+ ms = tv2ms(&afhi->chunk_tv) * afhi->chunks_total;
+ afhi->bitrate = ret / ms;
+ return 1;
+}
+
+/**
+ * The init function of the ogg/opus audio format handler.
+ *
+ * \param afh Pointer to the struct to initialize.
+ */
+void opus_afh_init(struct audio_format_handler *afh)
+{
+ afh->get_file_info = opus_get_file_info,
+ afh->suffixes = opus_suffixes;
+}
--- /dev/null
+/* Copyright (C)2012 Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file opus_common.c Common functions of the opus decoder and audio format
+ * handler.
+ */
+
+#include <ogg/ogg.h>
+
+#include "para.h"
+#include "error.h"
+#include "opus_common.h"
+#include "portable_io.h"
+
+struct packet {
+ const char *data;
+ int maxlen;
+ int pos;
+};
+
+static int read_chars(struct packet *p, unsigned char *str, int nb_chars)
+{
+ int i;
+
+ if (p->pos > p->maxlen - nb_chars)
+ return 0;
+ for (i = 0; i < nb_chars; i++)
+ str[i] = p->data[p->pos++];
+ return 1;
+}
+
+static int read_uint32(struct packet *p, ogg_uint32_t *val)
+{
+ if (p->pos > p->maxlen - 4)
+ return 0;
+ *val = read_u32(p->data + p->pos);
+ p->pos += 4;
+ return 1;
+}
+
+static int read_uint16(struct packet *p, ogg_uint16_t *val)
+{
+ if (p->pos > p->maxlen - 2)
+ return 0;
+ *val = read_u16(p->data + p->pos);
+ p->pos += 2;
+ return 1;
+}
+
+/**
+ * Get metadata of an opus stream.
+ *
+ * This is called from both the audio format handler (which passes ogg packet
+ * 0) and from the decoder.
+ *
+ * \param packet Start of the packet.
+ * \param len Number of bytes.
+ * \param h Result.
+ *
+ * \return Standard.
+ */
+int opus_parse_header(const char *packet, int len, struct opus_header *h)
+{
+ int i;
+ char str[9];
+ struct packet p;
+ unsigned char ch, channel_mapping;
+ ogg_uint16_t shortval;
+
+ p.data = packet;
+ p.maxlen = len;
+ p.pos = 0;
+ str[8] = 0;
+ if (len < 19)
+ return -E_OPUS_HEADER;
+ read_chars(&p, (unsigned char*)str, 8);
+ if (memcmp(str, "OpusHead", 8) != 0)
+ return -E_OPUS_HEADER;
+
+ if (!read_chars(&p, &ch, 1))
+ return -E_OPUS_HEADER;
+ h->version = ch;
+ if((h->version & 240) != 0) /* Only major version 0 supported. */
+ return -E_OPUS_HEADER;
+
+ if (!read_chars(&p, &ch, 1))
+ return -E_OPUS_HEADER;
+ h->channels = ch;
+ if (h->channels == 0)
+ return -E_OPUS_HEADER;
+
+ if (!read_uint16(&p, &shortval))
+ return -E_OPUS_HEADER;
+ h->preskip = shortval;
+
+ if (!read_uint32(&p, &h->input_sample_rate))
+ return -E_OPUS_HEADER;
+
+ if (!read_uint16(&p, &shortval))
+ return -E_OPUS_HEADER;
+ h->gain = (short)shortval;
+
+ if (!read_chars(&p, &ch, 1))
+ return -E_OPUS_HEADER;
+ channel_mapping = ch;
+
+ if (channel_mapping != 0) {
+ if (!read_chars(&p, &ch, 1))
+ return -E_OPUS_HEADER;
+
+ if (ch < 1)
+ return -E_OPUS_HEADER;
+ h->nb_streams = ch;
+
+ if (!read_chars(&p, &ch, 1))
+ return -E_OPUS_HEADER;
+
+ if (ch > h->nb_streams || (ch + h->nb_streams) > 255)
+ return -E_OPUS_HEADER;
+ h->nb_coupled = ch;
+
+ /* Multi-stream support */
+ for (i = 0; i < h->channels; i++) {
+ if (!read_chars(&p, &h->stream_map[i], 1))
+ return -E_OPUS_HEADER;
+ if (h->stream_map[i] > (h->nb_streams + h->nb_coupled)
+ && h->stream_map[i] != 255)
+ return -E_OPUS_HEADER;
+ }
+ } else {
+ if (h->channels > 2)
+ return -E_OPUS_HEADER;
+ h->nb_streams = 1;
+ h->nb_coupled = h->channels > 1;
+ h->stream_map[0] = 0;
+ h->stream_map[1] = 1;
+ }
+ /*
+ * For version 0/1 we know there won't be any more data so reject any
+ * that have data past the end.
+ */
+ if ((h->version == 0 || h->version == 1) && p.pos != len)
+ return -E_OPUS_HEADER;
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/**
+ * \file opus_common.h
+ * Common declarations for the opus decoder and audio format handler.
+ */
+
+/** Various bits stored in the header of an opus stream. */
+struct opus_header {
+ /** lower 4 bits of the version byte, must be 0. */
+ int version;
+ /** 1..255 */
+ int channels;
+ /** Number of bytes to skip from the beginning. */
+ int preskip;
+ /** Sample rate of the input stream, used by the audio format handler. */
+ ogg_uint32_t input_sample_rate;
+ /** In dB, should be zero whenever possible. */
+ int gain;
+ /** Number of logical streams (usually 1). */
+ int nb_streams;
+ /** Number of streams to decode as 2 channel streams. */
+ int nb_coupled;
+ /** Mapping from coded channels to output channels. */
+ unsigned char stream_map[255];
+};
+
+int opus_parse_header(const char *packet, int len, struct opus_header *h);
--- /dev/null
+/*
+ * Copyright (c) 2002-2007 Jean-Marc Valin
+ * Copyright (c) 2008 CSIRO
+ * Copyright (c) 2007-2012 Xiph.Org Foundation
+ * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file opusdec_filter.c The ogg/opus decoder. */
+
+/* This file is based on opusdec.c, by Jean-Marc Valin, see below. */
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
+ * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/* Silence gcc warning caused by including opus.h */
+#if !defined(__STDC_VERSION__)
+#define __STDC_VERSION__ 0
+#endif
+
+#include <regex.h>
+#include <opus/opus.h>
+#include <opus/opus_multistream.h>
+#include <ogg/ogg.h>
+#include <math.h>
+
+#include "para.h"
+#include "list.h"
+#include "sched.h"
+#include "ggo.h"
+#include "buffer_tree.h"
+#include "filter.h"
+#include "error.h"
+#include "string.h"
+#include "opus_common.h"
+
+/** 120ms at 48000 */
+#define MAX_FRAME_SIZE (960*6)
+
+struct opusdec_context {
+ OpusMSDecoder *st;
+ opus_int64 packet_count;
+ int total_links;
+ bool stream_init;
+ ogg_sync_state oy;
+ ogg_stream_state os;
+ ogg_page ogg_page;
+ bool eos;
+ int channels;
+ int preskip;
+ bool have_opus_stream;
+ ogg_int32_t opus_serialno;
+};
+
+static int opusdec_execute(struct btr_node *btrn, const char *cmd,
+ char **result)
+{
+ struct filter_node *fn = btr_context(btrn);
+ struct opusdec_context *ctx = fn->private_data;
+
+ return decoder_execute(cmd, 48000, ctx->channels, result);
+}
+
+static void opusdec_open(struct filter_node *fn)
+{
+ struct opusdec_context *ctx = para_calloc(sizeof(*ctx));
+
+ ogg_sync_init(&ctx->oy);
+ fn->private_data = ctx;
+}
+
+static void opusdec_close(struct filter_node *fn)
+{
+ struct opusdec_context *ctx = fn->private_data;
+
+ if (ctx->st) {
+ opus_multistream_decoder_destroy(ctx->st);
+ if (ctx->stream_init)
+ ogg_stream_clear(&ctx->os);
+ ogg_sync_clear(&ctx->oy);
+ }
+ free(ctx);
+ fn->private_data = NULL;
+}
+
+/* Process an Opus header and setup the opus decoder based on it. */
+static int opusdec_init(ogg_packet *op, struct opusdec_context *ctx)
+{
+ int ret;
+ struct opus_header header;
+
+ ctx->st = NULL;
+ ret = opus_parse_header((char *)op->packet, op->bytes, &header);
+ if (ret < 0)
+ return ret;
+ PARA_INFO_LOG("detected header v%d\n", header.version);
+ ctx->channels = header.channels;
+ ctx->preskip = header.preskip;
+ ctx->st = opus_multistream_decoder_create(48000, header.channels,
+ header.nb_streams, header.nb_coupled, header.stream_map, &ret);
+ if (ret != OPUS_OK || !ctx->st) {
+ PARA_ERROR_LOG("%s\n", opus_strerror(ret));
+ return -E_CREATE_OPUS_DECODER;
+ }
+ if (header.gain != 0) {
+ ret = opus_multistream_decoder_ctl(ctx->st,
+ OPUS_SET_GAIN(header.gain));
+ if (ret != OPUS_OK) {
+ PARA_ERROR_LOG("%s\n", opus_strerror(ret));
+ return -E_OPUS_SET_GAIN;
+ }
+ PARA_INFO_LOG("playback gain: %fdB\n", header.gain / 256.);
+ }
+ PARA_INFO_LOG("%d channel(s), 48KHz\n", ctx->channels);
+ return 1;
+}
+
+static void opusdec_add_output(short *pcm, int frames_available,
+ struct btr_node *btrn, struct opusdec_context *ctx)
+{
+ int tmp_skip, num_frames, bytes;
+
+ tmp_skip = PARA_MIN(ctx->preskip, frames_available);
+ ctx->preskip -= tmp_skip;
+ num_frames = frames_available - tmp_skip;
+ if (num_frames <= 0)
+ return;
+ bytes = sizeof(short) * num_frames * ctx->channels;
+
+ if (tmp_skip > 0) {
+ short *in = pcm + ctx->channels * tmp_skip;
+ short *out = para_malloc(bytes);
+ memcpy(out, in, bytes);
+ free(pcm);
+ pcm = out;
+ }
+ btr_add_output((char *)pcm, bytes, btrn);
+}
+
+/* returns > 1 if packet was decoded, 0 if it was ignored, negative on errors. */
+static int decode_packet(struct opusdec_context *ctx, ogg_packet *op,
+ struct btr_node *btrn)
+{
+ int ret;
+ short *output;
+
+ /*
+ * OggOpus streams are identified by a magic string in the initial
+ * stream header.
+ */
+ if (op->b_o_s && op->bytes >= 8 && !memcmp(op->packet, "OpusHead", 8)) {
+ if (!ctx->have_opus_stream) {
+ ctx->opus_serialno = ctx->os.serialno;
+ ctx->have_opus_stream = true;
+ ctx->packet_count = 0;
+ ctx->eos = false;
+ ctx->total_links++;
+ } else
+ PARA_NOTICE_LOG("ignoring opus stream %llu\n",
+ (long long unsigned)ctx->os.serialno);
+ }
+ if (!ctx->have_opus_stream || ctx->os.serialno != ctx->opus_serialno)
+ return 0;
+ /* If first packet in a logical stream, process the Opus header. */
+ if (ctx->packet_count == 0)
+ return opusdec_init(op, ctx);
+ if (ctx->packet_count == 1)
+ return 1;
+ /* don't care for anything except opus eos */
+ if (op->e_o_s && ctx->os.serialno == ctx->opus_serialno)
+ ctx->eos = true;
+ output = para_malloc(sizeof(short) * MAX_FRAME_SIZE * ctx->channels);
+ ret = opus_multistream_decode(ctx->st, (unsigned char *)op->packet,
+ op->bytes, output, MAX_FRAME_SIZE, 0);
+ if (ret < 0) {
+ PARA_ERROR_LOG("%s\n", opus_strerror(ret));
+ free(output);
+ return -E_OPUS_DECODE;
+ }
+ opusdec_add_output(output, ret, btrn, ctx);
+ return 1;
+}
+
+static int opusdec_post_select(__a_unused struct sched *s, struct task *t)
+{
+ struct filter_node *fn = container_of(t, struct filter_node, task);
+ struct opusdec_context *ctx = fn->private_data;
+ struct btr_node *btrn = fn->btrn;
+ int ret;
+ char *btr_buf, *data;
+ size_t nbytes;
+ ogg_packet op;
+
+ ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
+ if (ret <= 0)
+ goto out;
+ btr_merge(btrn, fn->min_iqs);
+ nbytes = btr_next_buffer(btrn, &btr_buf);
+ nbytes = PARA_MIN(nbytes, (size_t)32768);
+ ret = 0;
+ if (nbytes == 0)
+ goto out;
+ data = ogg_sync_buffer(&ctx->oy, nbytes);
+ memcpy(data, btr_buf, nbytes);
+ btr_consume(btrn, nbytes);
+ ogg_sync_wrote(&ctx->oy, nbytes);
+ for (;;) { /* loop over all ogg pages we got */
+ ret = 0;
+ if (ogg_sync_pageout(&ctx->oy, &ctx->ogg_page) != 1)
+ goto out;
+ if (!ctx->stream_init) {
+ ogg_stream_init(&ctx->os, ogg_page_serialno(&ctx->ogg_page));
+ ctx->stream_init = true;
+ }
+ if (ogg_page_serialno(&ctx->ogg_page) != ctx->os.serialno)
+ ogg_stream_reset_serialno(&ctx->os,
+ ogg_page_serialno(&ctx->ogg_page));
+ /* Add page to the bitstream */
+ ogg_stream_pagein(&ctx->os, &ctx->ogg_page);
+ for (;;) { /* loop over all opus packets */
+ ret = ogg_stream_packetout(&ctx->os, &op);
+ if (ret != 1)
+ break;
+ ret = decode_packet(ctx, &op, btrn);
+ if (ret < 0)
+ goto out;
+ ctx->packet_count++;
+ if (ctx->eos)
+ ctx->have_opus_stream = false;
+ }
+ }
+out:
+ if (ret < 0)
+ btr_remove_node(&fn->btrn);
+ return ret;
+}
+
+/**
+ * The init function of the opusdec filter.
+ *
+ * \param f Pointer to the filter struct to initialize.
+ *
+ * \sa filter::init.
+ */
+void opusdec_filter_init(struct filter *f)
+{
+ f->open = opusdec_open;
+ f->close = opusdec_close;
+ f->pre_select = generic_filter_pre_select;
+ f->post_select = opusdec_post_select;
+ f->execute = opusdec_execute;
+}
dev = "/dev/mixer";
PARA_INFO_LOG("opening %s\n", dev);
ret = para_open(dev, O_RDWR, 42);
- if (ret < 0)
+ if (ret < 0) {
+ PARA_ERROR_LOG("could not open %s\n", dev);
return ret;
+ }
h = para_malloc(sizeof(*h));
h->fd = ret;
*handle = h;
return ret;
}
-static void oss_post_select(__a_unused struct sched *s,
+static int oss_post_select(__a_unused struct sched *s,
struct task *t)
{
struct writer_node *wn = container_of(t, struct writer_node, task);
ret = oss_init(wn, rate, ch, format);
if (ret < 0)
goto out;
- return;
+ return 0;
}
btr_merge(btrn, wn->min_iqs);
bytes = btr_next_buffer(btrn, &data);
btr_consume(btrn, ret);
ret = 0;
out:
- t->error = ret;
if (ret < 0)
btr_remove_node(&wn->btrn);
+ return ret;
}
__malloc static void *oss_parse_config_or_die(int argc, char **argv)
w->post_select = oss_post_select;
w->parse_config_or_die = oss_parse_config_or_die;
w->free_config = oss_free_config;
- w->help = (struct ggo_help) {
- .short_help = oss_write_args_info_help,
- .detailed_help = oss_write_args_info_detailed_help
- };
+ w->help = (struct ggo_help)DEFINE_GGO_HELP(oss_write);
oss_write_cmdline_parser_free(&dummy);
}
sched_request_timeout_ms(50, s);
}
-static void osx_write_post_select(__a_unused struct sched *s, struct task *t)
+static int osx_write_post_select(__a_unused struct sched *s, struct task *t)
{
struct writer_node *wn = container_of(t, struct writer_node, task);
struct private_osx_write_data *powd = wn->private_data;
if (!powd) {
ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
if (ret == 0)
- return;
+ return 0;
if (ret < 0)
goto fail;
ret = core_audio_init(wn);
ret = 0;
mutex_unlock(powd->mutex);
if (ret >= 0)
- return;
+ return 0;
fail:
assert(ret < 0);
if (powd && powd->callback_btrn) {
}
btr_remove_node(&wn->btrn);
PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
- t->error = ret;
+ return ret;
}
/**
w->post_select = osx_write_post_select;
w->parse_config_or_die = osx_write_parse_config_or_die;
w->free_config = osx_free_config;
- w->help = (struct ggo_help) {
- .short_help = osx_write_args_info_help,
- .detailed_help = osx_write_args_info_detailed_help
- };
+ w->help = (struct ggo_help)DEFINE_GGO_HELP(osx_write);
osx_write_cmdline_parser_free(&dummy);
}
int para_exec_cmdline_pid(pid_t *pid, const char *cmdline, int *fds);
/* time */
-int tv_diff(const struct timeval *b, const struct timeval *a, struct timeval *diff);
-long unsigned tv2ms(const struct timeval*);
-void d2tv(double, struct timeval*);
-void tv_add(const struct timeval*, const struct timeval *, struct timeval *);
-void tv_scale(const unsigned long, const struct timeval *, struct timeval *);
+int tv_diff(const struct timeval *b, const struct timeval *a,
+ struct timeval *diff);
+long unsigned tv2ms(const struct timeval *tv);
+void d2tv(double x, struct timeval *tv);
+void tv_add(const struct timeval *a, const struct timeval *b,
+ struct timeval *sum);
+void tv_scale(const unsigned long mult, const struct timeval *tv,
+ struct timeval *result);
void tv_divide(const unsigned long divisor, const struct timeval *tv,
- struct timeval *result);
+ struct timeval *result);
int tv_convex_combination(const long a, const struct timeval *tv1,
const long b, const struct timeval *tv2,
struct timeval *result);
-void ms2tv(const long unsigned n, struct timeval *tv);
+void ms2tv(long unsigned n, struct timeval *tv);
void compute_chunk_time(long unsigned chunk_num,
struct timeval *chunk_tv, struct timeval *stream_start,
struct timeval *result);
+struct timeval *clock_get_realtime(struct timeval *tv);
/** The enum of all status items. */
enum status_items {STATUS_ITEM_ENUM NUM_STAT_ITEMS};
/** \file play.c Paraslash's standalone player. */
#include <regex.h>
-#include <sys/time.h>
#include <fnmatch.h>
#include <signal.h>
exit(EXIT_FAILURE);
}
-/** Description to be included in the --detailed-help output. */
-#define PP_DESC \
-"para_play is a command line audio player.\n" \
-"\n" \
-"It operates either in command mode or in insert mode. In insert mode it\n" \
-"presents a prompt and allows to enter para_play commands like stop, play, pause\n" \
-"etc. In command mode, the current audio file is shown and the program reads\n" \
-"single key strokes from stdin. Keys may be mapped to para_play commands.\n" \
-"Whenever a mapped key is pressed, the associated command is executed.\n" \
-
__noreturn static void print_help_and_die(void)
{
- int d = conf.detailed_help_given;
- const char **p = d? play_args_info_detailed_help
- : play_args_info_help;
-
-// printf_or_die("%s\n\n", PLAY_CMDLINE_PARSER_PACKAGE "-"
-// PLAY_CMDLINE_PARSER_VERSION);
-
- printf_or_die("%s\n\n", play_args_info_usage);
- if (d)
- printf_or_die("%s\n", PP_DESC);
- for (; *p; p++)
- printf_or_die("%s\n", *p);
+ struct ggo_help help = DEFINE_GGO_HELP(play);
+ unsigned flags = conf.detailed_help_given?
+ GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
+
+ ggo_print_help(&help, flags);
+ printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
exit(0);
}
.print_errors = 1
};
- if (play_cmdline_parser_ext(argc, argv, &conf, ¶ms))
- exit(EXIT_FAILURE);
- HANDLE_VERSION_FLAG("play", conf);
+ play_cmdline_parser_ext(argc, argv, &conf, ¶ms);
+ loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ version_handle_flag("play", conf.version_given);
if (conf.help_given || conf.detailed_help_given)
print_help_and_die();
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
if (conf.config_file_given)
config_file = para_strdup(conf.config_file_arg);
else {
params.initialize = 0;
params.check_required = 1;
play_cmdline_parser_config_file(config_file, &conf, ¶ms);
+ loglevel = get_loglevel_by_name(conf.loglevel_arg);
}
for (i = 0; i < conf.key_map_given; i++) {
char *s = strchr(conf.key_map_arg[i] + 1, ':');
static int play_i9e_line_handler(char *line)
{
- struct play_task *pt = &play_task;
- int ret;
-
- if (line == NULL || !*line)
- return 0;
- ret = run_command(line, pt);
- if (ret < 0)
- return ret;
- return 0;
+ return run_command(line, &play_task);
}
static int play_i9e_key_handler(int key)
char *history_file;
struct sigaction act;
- PARA_NOTICE_LOG("\n%s\n", VERSION_TEXT("play"));
+ PARA_NOTICE_LOG("\n%s\n", version_text("play"));
if (conf.history_file_given)
history_file = para_strdup(conf.history_file_arg);
else {
);
}
-static void play_post_select(struct sched *s, struct task *t)
+static int play_post_select(struct sched *s, struct task *t)
{
struct play_task *pt = container_of(t, struct play_task, task);
int ret;
ret = eof_cleanup(pt);
if (ret < 0) {
pt->rq = CRT_TERM_RQ;
- return;
+ return 0;
}
ret = session_post_select(s, t);
if (ret < 0)
}
ret = 1;
out:
- t->error = ret;
+ return ret;
}
/**
filter_init();
writer_init();
- gettimeofday(now, NULL);
+ clock_get_realtime(now);
sched.default_timeout.tv_sec = 5;
parse_config_or_die(argc, argv);
if (ret < 0)
goto err;
playlist->length = 0;
- ret = for_each_line_ro(playlist_def.data, playlist_def.size,
- add_playlist_entry, playlist);
+ ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+ playlist_def.size, add_playlist_entry, playlist);
osl_close_disk_object(&playlist_def);
if (ret < 0)
goto err;
ret = para_printf(pb, "checking playlist %s...\n", playlist_name);
if (ret < 0)
return ret;
- ret = for_each_line_ro(playlist_def.data, playlist_def.size,
- check_playlist_path, pb);
+ ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+ playlist_def.size, check_playlist_path, pb);
}
osl_close_disk_object(&playlist_def);
return ret;
ret = pl_get_def_by_name(current_playlist.name, &playlist_def);
if (ret < 0)
return ret;
- ret = for_each_line_ro(playlist_def.data, playlist_def.size,
- search_path, new_path);
+ ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+ playlist_def.size, search_path, new_path);
osl_close_disk_object(&playlist_def);
is_admissible = (ret < 0);
if (was_admissible && is_admissible)
struct prebuffer_filter_args_info *conf = ppd->conf;
struct timeval diff;
- t->error = 0;
if (iqs == 0)
return;
if (ppd->barrier.tv_sec == 0) {
free(fn->private_data);
}
-static void prebuffer_post_select(__a_unused struct sched *s, struct task *t)
+static int prebuffer_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct btr_node *btrn = fn->btrn;
struct private_prebuffer_data *ppd = fn->private_data;
struct prebuffer_filter_args_info *conf = ppd->conf;
- t->error = 0;
if (ppd->barrier.tv_sec == 0)
- return;
+ return 0;
if (tv_diff(now, &ppd->barrier, NULL) < 0)
- return;
+ return 0;
if (iqs < conf->size_arg)
- return;
- btr_splice_out_node(btrn);
- t->error = -E_PREBUFFER_SUCCESS;
+ return 0;
+ btr_splice_out_node(fn->btrn);
+ return -E_PREBUFFER_SUCCESS;
}
static int prebuffer_parse_config(int argc, char **argv, void **config)
f->free_config = prebuffer_free_config;
f->pre_select = prebuffer_pre_select;
f->post_select = prebuffer_post_select;
- f->help = (struct ggo_help) {
- .short_help = prebuffer_filter_args_info_help,
- .detailed_help = prebuffer_filter_args_info_detailed_help
- };
+ f->help = (struct ggo_help)DEFINE_GGO_HELP(prebuffer_filter);
}
__noreturn static void print_help_and_die(void)
{
- int d = conf.detailed_help_given;
- const char **p = d? recv_args_info_detailed_help
- : recv_args_info_help;
-
- printf_or_die("%s\n\n", RECV_CMDLINE_PARSER_PACKAGE "-"
- RECV_CMDLINE_PARSER_VERSION);
- printf_or_die("%s\n\n", recv_args_info_usage);
- for (; *p; p++)
- printf_or_die("%s\n", *p);
- print_receiver_helps(d);
- exit(0);
-}
+ struct ggo_help h = DEFINE_GGO_HELP(recv);
+ bool d = conf.detailed_help_given;
-static void *parse_config(int argc, char *argv[], int *receiver_num)
-{
- if (recv_cmdline_parser(argc, argv, &conf))
- return NULL;
- HANDLE_VERSION_FLAG("recv", conf);
- if (conf.help_given || conf.detailed_help_given)
- print_help_and_die();
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
- return check_receiver_arg(conf.receiver_arg, receiver_num);
+ ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+ print_receiver_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
+ exit(0);
}
/**
struct stdout_task sot;
static struct sched s;
- s.default_timeout.tv_sec = 1;
- s.default_timeout.tv_usec = 0;
+ recv_cmdline_parser(argc, argv, &conf);
+ loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ version_handle_flag("recv", conf.version_given);
+ recv_init();
+ if (conf.help_given || conf.detailed_help_given)
+ print_help_and_die();
- memset(&sot, 0, sizeof(struct stdout_task));
memset(&rn, 0, sizeof(struct receiver_node));
- recv_init();
- ret = -E_RECV_SYNTAX;
- rn.conf = parse_config(argc, argv, &receiver_num);
+ rn.conf = check_receiver_arg(conf.receiver_arg, &receiver_num);
if (!rn.conf) {
- PARA_EMERG_LOG("parse failed\n");
+ PARA_EMERG_LOG("invalid receiver specifier\n");
+ ret = -E_RECV_SYNTAX;
goto out;
}
r = &receivers[receiver_num];
goto out;
r_opened = 1;
+ memset(&sot, 0, sizeof(struct stdout_task));
sot.btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.parent = rn.btrn, .name = "stdout"));
stdout_set_defaults(&sot);
sprintf(rn.task.status, "%s", r->name);
register_task(&s, &rn.task);
+ s.default_timeout.tv_sec = 1;
+ s.default_timeout.tv_usec = 0;
ret = schedule(&s);
out:
if (r_opened)
/**
* The command line parser of the receiver.
*
- * It should check whether the command line options given by \a argc and \a
- * argv are valid. On success, it should return a pointer to the
- * receiver-specific configuration data determined by \a argc and \a argv.
- * Note that this might be called more than once with different values of
- * \a argc and \a argv.
+ * It should check whether the command line options given by \a argc
+ * and \a argv are valid. On success, it should return a pointer to
+ * the receiver-specific configuration data determined by \a argc and
+ * \a argv. Note that this might be called more than once with
+ * different values of \a argc and \a argv.
*/
void *(*parse_config)(int argc, char **argv);
/**
*
* \sa select(2), struct receiver.
*/
- void (*post_select)(struct sched *s, struct task *t);
+ int (*post_select)(struct sched *s, struct task *t);
/** The two help texts of this receiver. */
struct ggo_help help;
void recv_init(void);
void *check_receiver_arg(char *ra, int *receiver_num);
-void print_receiver_helps(int detailed);
+void print_receiver_helps(unsigned flags);
int generic_recv_pre_select(struct sched *s, struct task *t);
/** \cond receiver */
/**
* Print out the help texts to all receivers.
*
- * \param detailed Whether the detailed help should be printed.
+ * \param flags Passed to \ref ggo_print_help().
*/
-void print_receiver_helps(int detailed)
+void print_receiver_helps(unsigned flags)
{
int i;
- printf_or_die("\nAvailable receivers: \n\t");
+ printf_or_die("\nAvailable receivers: ");
FOR_EACH_RECEIVER(i)
printf_or_die("%s%s", i? " " : "", receivers[i].name);
- printf_or_die("\n\n");
+ printf_or_die("\n");
FOR_EACH_RECEIVER(i) {
struct receiver *r = receivers + i;
if (!r->help.short_help)
continue;
- printf_or_die("Options for %s:\n", r->name);
- ggo_print_help(&r->help, detailed);
+ printf_or_die("\n%s: %s", r->name,
+ r->help.purpose);
+ ggo_print_help(&r->help, flags);
}
}
return data.input_frames_used;
}
-static void resample_post_select(__a_unused struct sched *s, struct task *t)
+static int resample_post_select(__a_unused struct sched *s, struct task *t)
{
int ret;
struct filter_node *fn = container_of(t, struct filter_node, task);
* btr exec mechanism for the destination samplerate and the
* channel count.
*/
- return btr_pushdown(btrn);
+ btr_pushdown(btrn);
+ return 0;
}
btr_merge(btrn, fn->min_iqs);
in_bytes = btr_next_buffer(btrn, (char **)&in);
goto out;
btr_consume(btrn, ret * 2 * ctx->channels);
btr_add_output((char *)out, num_frames * 2 * ctx->channels, btrn);
- return;
+ return 0;
out:
if (ret < 0) {
- t->error = ret;
btr_remove_node(&fn->btrn);
/* This releases the check_wav btr node */
check_wav_post_select(ctx->cwc);
}
+ return ret;
}
static int resample_parse_config(int argc, char **argv, void **config)
f->parse_config = resample_parse_config;
f->free_config = resample_free_config;
f->execute = resample_execute;
- f->help = (struct ggo_help) {
- .short_help = resample_filter_args_info_help,
- .detailed_help = resample_filter_args_info_detailed_help
- };
+ f->help = (struct ggo_help)DEFINE_GGO_HELP(resample_filter);
}
#include <regex.h>
#include <assert.h>
-#include <sys/time.h>
#include "para.h"
#include "ipc.h"
static void sched_preselect(struct sched *s)
{
struct task *t, *tmp;
+
list_for_each_entry_safe(t, tmp, &s->pre_select_list, pre_select_node) {
- if (t->error < 0) {
- unregister_task(t);
- continue;
- }
- if (t->notification != 0) {
+ if (t->notification != 0)
sched_min_delay(s);
- break;
- }
- if (!t->pre_select)
- continue;
- t->pre_select(s, t);
- if (timeout_is_zero(s))
- break;
+ if (t->pre_select)
+ t->pre_select(s, t);
}
}
static inline void call_post_select(struct sched *s, struct task *t)
{
#ifndef SCHED_DEBUG
- t->post_select(s, t);
+ t->error = t->post_select(s, t);
#else
struct timeval t1, t2, diff;
unsigned long pst;
- gettimeofday(&t1, NULL);
- t->post_select(s, t);
- gettimeofday(&t2, NULL);
+ clock_get_realtime(&t1);
+ t->error = t->post_select(s, t);
+ clock_get_realtime(&t2);
tv_diff(&t1, &t2, &diff);
pst = tv2ms(&diff);
if (pst > 50)
FD_ZERO(&s->wfds);
s->select_timeout = s->default_timeout;
s->max_fileno = -1;
- gettimeofday(now, NULL);
+ clock_get_realtime(now);
sched_preselect(s);
- if (list_empty(&s->pre_select_list) && list_empty(&s->post_select_list))
- return 0;
- if (!timeout_is_zero(s)) {
- ret = s->select_function(s->max_fileno + 1, &s->rfds, &s->wfds,
- &s->select_timeout);
- if (ret < 0)
- return ret;
- if (ret == 0) {
- /*
- * APUE: Be careful not to check the descriptor sets on return
- * unless the return value is greater than zero. The return
- * state of the descriptor sets is implementation dependent if
- * either a signal is caught or the timer expires.
- */
- FD_ZERO(&s->rfds);
- FD_ZERO(&s->wfds);
- }
- gettimeofday(now, NULL);
- } else {
+ ret = s->select_function(s->max_fileno + 1, &s->rfds, &s->wfds,
+ &s->select_timeout);
+ if (ret < 0)
+ return ret;
+ if (ret == 0) {
+ /*
+ * APUE: Be careful not to check the descriptor sets on return
+ * unless the return value is greater than zero. The return
+ * state of the descriptor sets is implementation dependent if
+ * either a signal is caught or the timer expires.
+ */
FD_ZERO(&s->rfds);
FD_ZERO(&s->wfds);
}
+ clock_get_realtime(now);
sched_post_select(s);
if (list_empty(&s->pre_select_list) && list_empty(&s->post_select_list))
return 0;
* Before registering a task to the scheduler, the task structure must be
* filled in properly by the caller.
*
- * If one of these functions sets \a t->error to a negative value, the
- * task gets unregistered automatically.
- *
- * \sa struct sched.
+ * \sa \ref sched.
*/
struct task {
/**
*/
void (*pre_select)(struct sched *s, struct task *t);
/**
- * The postselect hook of \a t.
+ * The post select hook of \a t.
*
- * Evaluate and act upon the results of the previous select call.
+ * Its purpose is to evaluate and act upon the results of the previous
+ * select call. If this function returns a negative value, the
+ * scheduler unregisters the task.
*/
- void (*post_select)(struct sched *s, struct task *t);
+ int (*post_select)(struct sched *s, struct task *t);
/** Whether this task is in error state. */
int error;
/** Position of the task in the pre_select list of the scheduler. */
* This is set by the scheduler at the beginning of its main loop. It may be
* used (read-only) from everywhere. As none of the functions called by the
* scheduler are allowed to block, this value should be accurate enough so that
- * there is no need to call gettimeofday() directly.
+ * there is no need to call clock_gettime() directly.
*/
extern struct timeval *now;
*/
#include <signal.h>
-#include <sys/time.h>
#include <regex.h>
#include <osl.h>
#include "signal.h"
#include "user_list.h"
#include "color.h"
+#include "ggo.h"
#include "version.h"
__printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
.print_errors = !conf.daemon_given
};
server_cmdline_parser_config_file(cf, &conf, ¶ms);
+ daemon_set_loglevel(conf.loglevel_arg);
conf.daemon_given = tmp;
}
if (conf.logfile_given) {
daemon_set_logfile(conf.logfile_arg);
daemon_open_log_or_die();
}
- daemon_set_loglevel(conf.loglevel_arg);
init_colors_or_die();
daemon_set_flag(DF_LOG_PID);
daemon_set_flag(DF_LOG_LL);
kill(mmd->afs_pid, SIGHUP);
}
-static void signal_post_select(struct sched *s, __a_unused struct task *t)
+static int signal_post_select(struct sched *s, __a_unused struct task *t)
{
int signum = para_next_signal(&s->rfds);
switch (signum) {
case 0:
- return;
+ return 0;
case SIGHUP:
handle_sighup();
break;
shm_detach(mmd);
exit(EXIT_FAILURE);
}
+ return 0;
}
static void init_signal_task(void)
para_fd_set(sct->listen_fd, &s->rfds, &s->max_fileno);
}
-static void command_post_select(struct sched *s, struct task *t)
+static int command_post_select(struct sched *s, struct task *t)
{
struct server_command_task *sct = container_of(t, struct server_command_task, task);
if (child_pid) {
close(new_fd);
/* parent keeps accepting connections */
- return;
+ return 0;
}
/* mmd might already have changed at this point */
free(chunk_table);
for (i = sct->argc - 1; i >= 0; i--)
memset(sct->argv[i], 0, strlen(sct->argv[i]));
sprintf(sct->argv[0], "para_server (serving %s)", peer_name);
- return handle_connect(new_fd, peer_name);
+ handle_connect(new_fd, peer_name);
+ /* never reached*/
out:
if (ret < 0)
PARA_CRIT_LOG("%s\n", para_strerror(-ret));
+ return 0;
}
static void init_server_command_task(int argc, char **argv)
return afs_server_socket[0];
}
+__noreturn static void print_help_and_die(void)
+{
+ struct ggo_help h = DEFINE_GGO_HELP(server);
+ bool d = conf.detailed_help_given;
+
+ ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+ exit(0);
+}
+
static void server_init(int argc, char **argv)
{
struct server_cmdline_parser_params params = {
init_random_seed_or_die();
/* parse command line options */
server_cmdline_parser_ext(argc, argv, &conf, ¶ms);
- HANDLE_VERSION_FLAG("server", conf);
+ daemon_set_loglevel(conf.loglevel_arg);
+ version_handle_flag("server", conf.version_given);
+ if (conf.help_given || conf.detailed_help_given)
+ print_help_and_die();
drop_privileges_or_die(conf.user_arg, conf.group_arg);
/* parse config file, open log and set defaults */
parse_config_or_die(0);
log_welcome("para_server");
init_ipc_or_die(); /* init mmd struct and mmd->lock */
/* make sure, the global now pointer is uptodate */
- gettimeofday(now, NULL);
+ clock_get_realtime(now);
set_server_start_time(now);
init_user_list(user_list_file);
/* become daemon */
int spx_process_header(unsigned char *packet, long bytes,
struct spx_header_info *shi);
-int spx_ctl(void *state, int request, void *ptr);
*
* \return Standard.
*/
-int spx_ctl(void *state, int request, void *ptr)
+static int spx_ctl(void *state, int request, void *ptr)
{
int ret = speex_decoder_ctl(state, request, ptr);
return ret;
}
-static void speexdec_post_select(__a_unused struct sched *s, struct task *t)
+static int speexdec_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct private_spxdec_data *psd = fn->private_data;
size_t nbytes;
next_buffer:
- t->error = 0;
ret = ns = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
btr_merge(btrn, fn->min_iqs);
if (!psd->shi.state) {
goto next_buffer;
ret = ns;
fail:
- if (ret < 0) {
- t->error = ret;
+ if (ret < 0)
btr_remove_node(&fn->btrn);
- }
+ return ret;
}
/**
struct stdin_task *sit = container_of(t, struct stdin_task, task);
int ret;
- t->error = 0;
ret = btr_node_status(sit->btrn, 0, BTR_NT_ROOT);
if (ret < 0)
sched_min_delay(s);
* appears to be readable, data is read from stdin and fed into the buffer
* tree.
*/
-static void stdin_post_select(struct sched *s, struct task *t)
+static int stdin_post_select(struct sched *s, struct task *t)
{
struct stdin_task *sit = container_of(t, struct stdin_task, task);
ssize_t ret;
size_t sz, n;
char *buf = NULL;
- t->error = 0;
ret = btr_node_status(sit->btrn, 0, BTR_NT_ROOT);
if (ret < 0)
goto err;
if (ret == 0)
- return;
+ return 0;
sz = btr_pool_get_buffer(sit->btrp, &buf);
if (sz == 0)
- return;
+ return 0;
+ if (sit->must_set_nonblock_flag) {
+ ret = mark_fd_nonblocking(STDIN_FILENO);
+ if (ret < 0)
+ goto err;
+ sit->must_set_nonblock_flag = false;
+ }
/*
* Do not use the maximal size to avoid having only a single buffer
* reference for the whole pool. This is bad because if that single
if (n > 0)
btr_add_output_pool(sit->btrp, n, sit->btrn);
if (ret >= 0)
- return;
+ return 0;
err:
btr_remove_node(&sit->btrn);
+ /* Revert to blocking mode if necessary. */
+ fcntl(STDIN_FILENO, F_SETFL, sit->fd_flags);
//btr_pool_free(sit->btrp);
- t->error = ret;
+ return ret;
}
/**
* \param sit The stdin task structure.
*
* This fills in the pre/post select function pointers of the task structure
- * given by \a sit. Moreover, the stdin file desctiptor is set to nonblocking
- * mode, and a buffer tree is created.
+ * given by \a sit and creates a buffer tree for I/O.
*/
void stdin_set_defaults(struct stdin_task *sit)
{
sit->task.post_select = stdin_post_select;
sit->btrp = btr_pool_new("stdin", 128 * 1024);
sprintf(sit->task.status, "stdin reader");
- ret = mark_fd_nonblocking(STDIN_FILENO);
- if (ret >= 0)
- return;
- PARA_EMERG_LOG("%s\n", para_strerror(-ret));
- exit(EXIT_FAILURE);
+ /*
+ * Both STDIN_FILENO and STDOUT_FILENO may refer to the same open file
+ * description (the terminal), and thus share the same file status
+ * flags. In order to not interfere with the stdout task, we only get
+ * the file status flags for STDIN here and save a copy. The nonblock
+ * flag is set later on the first read.
+ */
+ ret = fcntl(STDIN_FILENO, F_GETFL);
+ if (ret < 0) {
+ PARA_EMERG_LOG("F_GETFL: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ sit->fd_flags = ret;
+ sit->must_set_nonblock_flag = (sit->fd_flags & O_NONBLOCK) == 0;
}
struct btr_node *btrn;
/** Use a buffer pool to minimize memcpy due to alignment problems. */
struct btr_pool *btrp;
+ /** The descriptor flags of STDIN at startup. */
+ int fd_flags;
+ /** Whether we have to set STDIN to nonblocking mode. */
+ bool must_set_nonblock_flag;
};
void stdin_set_defaults(struct stdin_task *sit);
struct stdout_task *sot = container_of(t, struct stdout_task, task);
int ret;
- t->error = 0;
ret = btr_node_status(sot->btrn, 0, BTR_NT_LEAF);
if (ret > 0)
para_fd_set(STDOUT_FILENO, &s->wfds, &s->max_fileno);
* This function writes input data from the buffer tree to stdout if \p
* STDOUT_FILENO is writable.
*/
-static void stdout_post_select(struct sched *s, struct task *t)
+static int stdout_post_select(struct sched *s, struct task *t)
{
struct stdout_task *sot = container_of(t, struct stdout_task, task);
struct btr_node *btrn = sot->btrn;
char *buf;
size_t sz;
- t->error = 0;
ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
if (ret < 0)
goto out;
if (ret == 0)
- return;
+ return 0;
if (!FD_ISSET(STDOUT_FILENO, &s->wfds))
- return;
+ return 0;
+ if (sot->must_set_nonblock_flag) {
+ ret = mark_fd_nonblocking(STDOUT_FILENO);
+ if (ret < 0)
+ goto out;
+ sot->must_set_nonblock_flag = false;
+ }
for (;;) {
sz = btr_next_buffer(btrn, &buf);
if (sz == 0)
btr_consume(btrn, ret);
}
out:
- if (ret < 0)
+ if (ret < 0) {
btr_remove_node(&sot->btrn);
- t->error = ret;
+ /* Revert to blocking mode if necessary. */
+ fcntl(STDOUT_FILENO, F_SETFL, sot->fd_flags);
+ }
+ return ret;
}
/**
* Initialize a stdout task structure with default values.
* \param sot The stdout task structure.
*
* This fills in the pre/post select function pointers of the task structure
- * given by \a sot and sets the stdout file descriptor to nonblocking mode.
+ * given by \a sot.
*/
void stdout_set_defaults(struct stdout_task *sot)
{
sot->task.pre_select = stdout_pre_select;
sot->task.post_select = stdout_post_select;
sprintf(sot->task.status, "stdout");
- ret = mark_fd_nonblocking(STDOUT_FILENO);
- if (ret >= 0)
- return;
- PARA_EMERG_LOG("%s\n", para_strerror(-ret));
- exit(EXIT_FAILURE);
+
+ /* See stdin.c for details. */
+ ret = fcntl(STDOUT_FILENO, F_GETFL);
+ if (ret < 0) {
+ PARA_EMERG_LOG("F_GETFL: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ sot->fd_flags = ret;
+ sot->must_set_nonblock_flag = (sot->fd_flags & O_NONBLOCK) == 0;
}
struct task task;
/** Stdout is always a leaf node in the buffer tree. */
struct btr_node *btrn;
+ /** The descriptor flags of STDOUT at startup. */
+ int fd_flags;
+ /** Whether we have to set STDOUT to nonblocking mode. */
+ bool must_set_nonblock_flag;
};
void stdout_set_defaults(struct stdout_task *sot);
/** \file string.c Memory allocation and string handling functions. */
-#include <sys/time.h> /* gettimeofday */
+#define _GNU_SOURCE
+
#include <pwd.h>
#include <sys/utsname.h> /* uname() */
+
#include <string.h>
#include <regex.h>
+#include <langinfo.h>
+#include <wchar.h>
+#include <wctype.h>
+
#include "para.h"
#include "string.h"
#include "error.h"
__printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap)
{
int ret;
- size_t size;
+ size_t size = 150;
va_list aq;
+ *result = para_malloc(size + 1);
va_copy(aq, ap);
- ret = vsnprintf(NULL, 0, fmt, aq);
+ ret = vsnprintf(*result, size, fmt, aq);
va_end(aq);
assert(ret >= 0);
+ if (ret < size) /* OK */
+ return ret;
size = ret + 1;
- *result = para_malloc(size);
+ *result = para_realloc(*result, size);
va_copy(aq, ap);
ret = vsnprintf(*result, size, fmt, aq);
va_end(aq);
}
/**
- * Used to distinguish between read-only and read-write mode.
+ * Call a custom function for each complete line.
+ *
+ * \param flags Any combination of flags defined in \ref for_each_line_flags.
+ * \param buf The buffer containing data separated by newlines.
+ * \param size The number of bytes in \a buf.
+ * \param line_handler The custom function.
+ * \param private_data Pointer passed to \a line_handler.
+ *
+ * For each complete line in \p buf, \p line_handler is called. The first
+ * argument to \p line_handler is (a copy of) the current line, and \p
+ * private_data is passed as the second argument. If the \p FELF_READ_ONLY
+ * flag is unset, a pointer into \a buf is passed to the line handler,
+ * otherwise a pointer to a copy of the current line is passed instead. This
+ * copy is freed immediately after the line handler returns.
+ *
+ * The function returns if \p line_handler returns a negative value or no more
+ * lines are in the buffer. The rest of the buffer (last chunk containing an
+ * incomplete line) is moved to the beginning of the buffer if FELF_READ_ONLY is
+ * unset.
*
- * \sa for_each_line(), for_each_line_ro().
+ * \return On success this function returns the number of bytes not handled to
+ * \p line_handler. The only possible error is a negative return value from the
+ * line handler. In this case processing stops and the return value of the line
+ * handler is returned to indicate failure.
+ *
+ * \sa \ref for_each_line_flags.
*/
-enum for_each_line_modes{
- /** Activate read-only mode. */
- LINE_MODE_RO,
- /** Activate read-write mode. */
- LINE_MODE_RW
-};
-
-static int for_each_complete_line(enum for_each_line_modes mode, char *buf,
- size_t size, line_handler_t *line_handler, void *private_data)
+int for_each_line(unsigned flags, char *buf, size_t size,
+ line_handler_t *line_handler, void *private_data)
{
char *start = buf, *end;
int ret, i, num_lines = 0;
} else
end = next_cr;
num_lines++;
- if (!line_handler) {
- start = ++end;
- continue;
- }
- if (mode == LINE_MODE_RO) {
- size_t s = end - start;
- char *b = para_malloc(s + 1);
- memcpy(b, start, s);
- b[s] = '\0';
-// PARA_NOTICE_LOG("b: %s, start: %s\n", b, start);
- ret = line_handler(b, private_data);
- free(b);
- } else {
- *end = '\0';
- ret = line_handler(start, private_data);
+ if (!(flags & FELF_DISCARD_FIRST) || start != buf) {
+ if (flags & FELF_READ_ONLY) {
+ size_t s = end - start;
+ char *b = para_malloc(s + 1);
+ memcpy(b, start, s);
+ b[s] = '\0';
+ ret = line_handler(b, private_data);
+ free(b);
+ } else {
+ *end = '\0';
+ ret = line_handler(start, private_data);
+ }
+ if (ret < 0)
+ return ret;
}
- if (ret < 0)
- return ret;
start = ++end;
}
- if (!line_handler || mode == LINE_MODE_RO)
- return num_lines;
i = buf + size - start;
- if (i && i != size)
+ if (i && i != size && !(flags & FELF_READ_ONLY))
memmove(buf, start, i);
return i;
}
-/**
- * Call a custom function for each complete line.
- *
- * \param buf The buffer containing data separated by newlines.
- * \param size The number of bytes in \a buf.
- * \param line_handler The custom function.
- * \param private_data Pointer passed to \a line_handler.
- *
- * If \p line_handler is \p NULL, the function returns the number of complete
- * lines in \p buf. Otherwise, \p line_handler is called for each complete
- * line in \p buf. The first argument to \p line_handler is the current line,
- * and \p private_data is passed as the second argument. The function returns
- * if \p line_handler returns a negative value or no more lines are in the
- * buffer. The rest of the buffer (last chunk containing an incomplete line)
- * is moved to the beginning of the buffer.
- *
- * \return If \p line_handler is not \p NULL, this function returns the number
- * of bytes not handled to \p line_handler on success, or the negative return
- * value of the \p line_handler on errors.
- *
- * \sa for_each_line_ro().
- */
-int for_each_line(char *buf, size_t size, line_handler_t *line_handler,
- void *private_data)
-{
- return for_each_complete_line(LINE_MODE_RW, buf, size, line_handler,
- private_data);
-}
-
-/**
- * Call a custom function for each complete line.
- *
- * \param buf Same meaning as in \p for_each_line().
- * \param size Same meaning as in \p for_each_line().
- * \param line_handler Same meaning as in \p for_each_line().
- * \param private_data Same meaning as in \p for_each_line().
- *
- * This function behaves like \p for_each_line(), but \a buf is left unchanged.
- *
- * \return On success, the function returns the number of complete lines in \p
- * buf, otherwise the (negative) return value of \p line_handler is returned.
- *
- * \sa for_each_line().
- */
-int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler,
- void *private_data)
-{
- return for_each_complete_line(LINE_MODE_RO, buf, size, line_handler,
- private_data);
-}
-
/** Return the hex characters of the lower 4 bits. */
#define hex(a) (hexchar[(a) & 15])
return NULL;
return safe_strdup(src + keylen + 1, len - keylen - 1);
}
+
+static bool utf8_mode(void)
+{
+ static bool initialized, have_utf8;
+
+ if (!initialized) {
+ char *info = nl_langinfo(CODESET);
+ have_utf8 = (info && strcmp(info, "UTF-8") == 0);
+ initialized = true;
+ PARA_INFO_LOG("%susing UTF-8 character encoding\n",
+ have_utf8? "" : "not ");
+ }
+ return have_utf8;
+}
+
+/*
+ * glibc's wcswidth returns -1 if the string contains a tab character, which
+ * makes the function next to useless. The two functions below are taken from
+ * mutt.
+ */
+
+#define IsWPrint(wc) (iswprint(wc) || wc >= 0xa0)
+
+static int mutt_wcwidth(wchar_t wc, size_t pos)
+{
+ int n;
+
+ if (wc == 0x09) /* tab */
+ return (pos | 7) + 1 - pos;
+ n = wcwidth(wc);
+ if (IsWPrint(wc) && n > 0)
+ return n;
+ if (!(wc & ~0x7f))
+ return 2;
+ if (!(wc & ~0xffff))
+ return 6;
+ return 10;
+}
+
+static size_t mutt_wcswidth(const wchar_t *s, size_t n)
+{
+ size_t w = 0;
+
+ while (n--)
+ w += mutt_wcwidth(*s++, w);
+ return w;
+}
+
+/**
+ * Skip a given number of cells at the beginning of a string.
+ *
+ * \param s The input string.
+ * \param cells_to_skip Desired number of cells that should be skipped.
+ * \param bytes_to_skip Result.
+ *
+ * This function computes how many input bytes must be skipped to advance a
+ * string by the given width. If the current character encoding is not UTF-8,
+ * this is simply the given number of cells, i.e. \a cells_to_skip. Otherwise,
+ * \a s is treated as a multibyte string and on successful return, \a s +
+ * bytes_to_skip points to the start of a multibyte string such that the total
+ * width of the multibyte characters that are skipped by advancing \a s that
+ * many bytes equals at least \a cells_to_skip.
+ *
+ * \return Standard.
+ */
+int skip_cells(const char *s, size_t cells_to_skip, size_t *bytes_to_skip)
+{
+ wchar_t wc;
+ mbstate_t ps;
+ size_t n, bytes_parsed, cells_skipped;
+
+ *bytes_to_skip = 0;
+ if (cells_to_skip == 0)
+ return 0;
+ if (!utf8_mode()) {
+ *bytes_to_skip = cells_to_skip;
+ return 0;
+ }
+ bytes_parsed = cells_skipped = 0;
+ memset(&ps, 0, sizeof(ps));
+ n = strlen(s);
+ while (cells_to_skip > cells_skipped) {
+ size_t mbret;
+
+ mbret = mbrtowc(&wc, s + bytes_parsed, n - bytes_parsed, &ps);
+ assert(mbret != 0);
+ if (mbret == (size_t)-1 || mbret == (size_t)-2)
+ return -ERRNO_TO_PARA_ERROR(EILSEQ);
+ bytes_parsed += mbret;
+ cells_skipped += mutt_wcwidth(wc, cells_skipped);
+ }
+ *bytes_to_skip = bytes_parsed;
+ return 1;
+}
+
+/**
+ * Compute the width of an UTF-8 string.
+ *
+ * \param s The string.
+ * \param result The width of \a s is returned here.
+ *
+ * If not in UTF8-mode. this function is just a wrapper for strlen(3).
+ * Otherwise \a s is treated as an UTF-8 string and its display width is
+ * computed. Note that this function may fail if the underlying call to
+ * mbsrtowcs(3) fails, so the caller must check the return value.
+ *
+ * \sa nl_langinfo(3), wcswidth(3).
+ *
+ * \return Standard.
+ */
+__must_check int strwidth(const char *s, size_t *result)
+{
+ const char *src = s;
+ mbstate_t state;
+ static wchar_t *dest;
+ size_t num_wchars;
+
+ /*
+ * Never call any log function here. This may result in an endless loop
+ * as para_gui's para_log() calls this function.
+ */
+
+ if (!utf8_mode()) {
+ *result = strlen(s);
+ return 0;
+ }
+ memset(&state, 0, sizeof(state));
+ *result = 0;
+ num_wchars = mbsrtowcs(NULL, &src, 0, &state);
+ if (num_wchars == (size_t)-1)
+ return -ERRNO_TO_PARA_ERROR(errno);
+ if (num_wchars == 0)
+ return 0;
+ dest = para_malloc(num_wchars * sizeof(*dest));
+ src = s;
+ memset(&state, 0, sizeof(state));
+ num_wchars = mbsrtowcs(dest, &src, num_wchars, &state);
+ assert(num_wchars > 0 && num_wchars != (size_t)-1);
+ *result = mutt_wcswidth(dest, num_wchars);
+ free(dest);
+ return 1;
+}
void *private_data;
};
+/**
+ * Controls the behavior of for_each_line().
+ *
+ * \sa for_each_line().
+ */
+enum for_each_line_flags {
+ /** Activate read-only mode. */
+ FELF_READ_ONLY = 1 << 0,
+ /** Don't call line handler for the first input line. */
+ FELF_DISCARD_FIRST = 1 << 1,
+};
+
+/** Used for \ref for_each_line(). */
+typedef int line_handler_t(char *, void *);
+int for_each_line(unsigned flags, char *buf, size_t size,
+ line_handler_t *line_handler, void *private_data);
+
/**
* Write the contents of a status item to a para_buffer.
*
__must_check __malloc char *para_homedir(void);
__malloc char *para_hostname(void);
__printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...);
-/** Used for for_each_line() and for_each_line_ro(). */
-typedef int line_handler_t(char *, void *);
-int for_each_line(char *buf, size_t size, line_handler_t *line_handler,
- void *private_data);
-int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler,
- void *private_data);
int para_atoi64(const char *str, int64_t *result);
int para_atoi32(const char *str, int32_t *value);
int get_loglevel_by_name(const char *txt);
int compute_word_num(const char *buf, const char *delim, int offset);
char *safe_strdup(const char *src, size_t len);
char *key_value_copy(const char *src, size_t len, const char *key);
+int skip_cells(const char *s, size_t cells_to_skip, size_t *result);
+__must_check int strwidth(const char *s, size_t *result);
*
* \return If \a b < \a a, this function returns -1, otherwise it returns 1.
*/
-int tv_diff(const struct timeval *b, const struct timeval *a, struct timeval *diff)
+int tv_diff(const struct timeval *b, const struct timeval *a,
+ struct timeval *diff)
{
int ret = 1;
* \param sum Contains the sum \a a + \a b on return.
*/
void tv_add(const struct timeval *a, const struct timeval *b,
- struct timeval *sum)
+ struct timeval *sum)
{
sum->tv_sec = a->tv_sec + b->tv_sec;
if (a->tv_usec + b->tv_usec >= 1000 * 1000) {
* \param result Contains \a mult * \a tv on return.
*/
void tv_scale(const unsigned long mult, const struct timeval *tv,
- struct timeval *result)
+ struct timeval *result)
{
uint64_t x = ((uint64_t)tv->tv_sec * 1000 * 1000 + tv->tv_usec) * mult;
* \param result Contains (1 / mult) * tv on return.
*/
void tv_divide(const unsigned long divisor, const struct timeval *tv,
- struct timeval *result)
+ struct timeval *result)
{
uint64_t x = ((uint64_t)tv->tv_sec * 1000 * 1000 + tv->tv_usec) / divisor;
tv_scale(chunk_num, chunk_tv, &tmp);
tv_add(&tmp, stream_start, result);
}
+
+/**
+ * Retrieve the time of the realtime clock.
+ *
+ * \param tv Where to store the result.
+ *
+ * Gets the current value of the system-wide real-time clock (identified by id
+ * \p CLOCK_REALTIME). If \a tv is \p NULL, the value is stored in a static
+ * buffer, otherwise it is stored at the location given by \a tv.
+ *
+ * \return This function aborts on errors. On success it returns a pointer to
+ * memory containing the current time.
+ *
+ * \sa clock_gettime(2), gettimeofday(2).
+ */
+struct timeval *clock_get_realtime(struct timeval *tv)
+{
+ static struct timeval user_friendly;
+
+ if (!tv)
+ tv = &user_friendly;
+#ifdef HAVE_CLOCK_GETTIME
+ {
+ struct timespec t;
+ int ret;
+
+ ret = clock_gettime(CLOCK_REALTIME, &t);
+ assert(ret == 0);
+ tv->tv_sec = t.tv_sec;
+ tv->tv_usec = t.tv_nsec / 1000;
+ }
+#else
+ #include <sys/time.h>
+ gettimeofday(tv, NULL);
+#endif /* HAVE_CLOCK_GETTIME */
+ return tv;
+}
return -E_RECV_EOF;
}
-static void udp_recv_post_select(__a_unused struct sched *s, struct task *t)
+static int udp_recv_post_select(__a_unused struct sched *s, struct task *t)
{
struct receiver_node *rn = container_of(t, struct receiver_node, task);
struct btr_node *btrn = rn->btrn;
}
ret = readv_ret;
out:
- if (ret >= 0)
- return;
- btr_remove_node(&rn->btrn);
- t->error = ret;
- close(rn->fd);
- rn->fd = -1;
+ if (ret < 0) {
+ btr_remove_node(&rn->btrn);
+ close(rn->fd);
+ rn->fd = -1;
+ }
+ return ret;
}
static void udp_recv_close(struct receiver_node *rn)
static void *udp_recv_parse_config(int argc, char **argv)
{
- int ret;
- struct udp_recv_args_info *tmp =
- para_calloc(sizeof(struct udp_recv_args_info));
-
- ret = udp_recv_cmdline_parser(argc, argv, tmp)? -E_UDP_SYNTAX : 1;
- if (ret >= 0)
- return tmp;
- free(tmp);
- return NULL;
+ struct udp_recv_args_info *tmp = para_calloc(sizeof(*tmp));
+ udp_recv_cmdline_parser(argc, argv, tmp);
+ return tmp;
}
/*
r->post_select = udp_recv_post_select;
r->parse_config = udp_recv_parse_config;
r->free_config = udp_recv_free_config;
- r->help = (struct ggo_help) {
- .short_help = udp_recv_args_info_help,
- .detailed_help = udp_recv_args_info_detailed_help
- };
+ r->help = (struct ggo_help)DEFINE_GGO_HELP(udp_recv);
udp_recv_cmdline_parser_free(&dummy);
}
#include <regex.h>
-#include <sys/time.h>
+#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/udp.h>
#include <net/if.h>
--- /dev/null
+/*
+ * Copyright (C) 2013 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file version.c Some helpers for printing version and copyright strings. */
+
+#include "para.h"
+
+/** \file version.h Macros for printing the version string. */
+
+#include "git-version.h"
+
+/**
+ * Get the raw git version string
+ *
+ * \return The string generated by the GIT-VERSION-GEN script. It is passed
+ * as a preprocessor define during compilation.
+ */
+__a_const const char *version_git(void)
+{
+ return GIT_VERSION;
+}
+
+/**
+ * Get the version string for an executable.
+ *
+ * \param pfx The program name (without the leading "para_").
+ *
+ * \return A statically allocated string which contains the program name, the
+ * git version and the codename. It must not be freed by the caller.
+ */
+const char *version_single_line(const char *pfx)
+{
+ static char buf[100];
+ snprintf(buf, sizeof(buf) - 1,
+ "para_%s " GIT_VERSION, pfx);
+ return buf;
+}
+
+/**
+ * Get the full version text.
+ *
+ * \param pfx See \ref version_single_line().
+ *
+ * \return A string containing the same text as returned by \ref
+ * version_single_line(), augmented by additional build information, a
+ * copyright text and the email address of the author.
+ *
+ * Like \ref version_single_line(), this string is stored in a statically
+ * allocated buffer and must not be freed.
+ */
+const char *version_text(const char *pfx)
+{
+ static char buf[512];
+
+ snprintf(buf, sizeof(buf) - 1, "%s\n"
+ "Copyright (C) 2013 Andre Noll\n"
+ "This is free software with ABSOLUTELY NO WARRANTY."
+ " See COPYING for details.\n"
+ "Report bugs to <maan@systemlinux.org>.\n"
+ "build date: " BUILD_DATE ",\n"
+ "build system: " UNAME_RS ",\n"
+ "compiler: " CC_VERSION ".\n",
+ version_single_line(pfx)
+ );
+ return buf;
+}
+
+/**
+ * Print the version text and exit successfully.
+ *
+ * \param pfx See \ref version_single_line().
+ * \param flag Whether --version was given.
+ *
+ * If \a flag is false, this function does nothing. Otherwise it prints the
+ * full version text as returned by \ref version_text() and exits successfully.
+ */
+void version_handle_flag(const char *pfx, bool flag)
+{
+ if (!flag)
+ return;
+ printf("%s", version_text(pfx));
+ exit(EXIT_SUCCESS);
+}
-/** \file version.h Macros for printing the version string. */
-
-#include "git-version.h"
-
-/** Version text printed by all executables if -V was given. */
-#define VERSION_TEXT(prefix) "para_" prefix " " PACKAGE_VERSION \
- " (" GIT_VERSION ": " CODENAME ")" "\n" \
- "Copyright (C) 2013 Andre Noll\n" \
- "This is free software with ABSOLUTELY NO WARRANTY." \
- " See COPYING for details.\n" \
- "Report bugs to <maan@systemlinux.org>.\n"
-
-/** Print out \p VERSION_TEXT and exit if version flag was given. */
-#define HANDLE_VERSION_FLAG(_prefix, _args_info_struct) \
- if (_args_info_struct.version_given) { \
- printf("%s", VERSION_TEXT(_prefix)); \
- exit(EXIT_SUCCESS); \
- }
+/** \file version.h Functions for printing the version string. */
+const char *version_git(void);
+const char *version_single_line(const char *pfx);
+const char *version_text(const char *pfx);
+void version_handle_flag(const char *pfx, bool flag);
}
}
-static void vss_post_select(struct sched *s, struct task *t)
+static int vss_post_select(struct sched *s, struct task *t)
{
int ret, i;
struct vss_task *vsst = container_of(t, struct vss_task, task);
-
if (mmd->sender_cmd_data.cmd_num >= 0) {
int num = mmd->sender_cmd_data.cmd_num,
sender_num = mmd->sender_cmd_data.sender_num;
(vss_next() && vss_playing()))
tv_add(now, &vsst->announce_tv, &vsst->data_send_barrier);
vss_send(vsst);
+ return 0;
}
/**
sched_min_delay(s);
}
-static void wav_post_select(__a_unused struct sched *s, struct task *t)
+static int wav_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct btr_node *btrn = fn->btrn;
char *header, *buf;
int32_t rate, ch;
- t->error = 0;
if (iqs == 0) {
ret = -E_WAV_EOF;
if (btr_no_parent(btrn))
goto err;
- return;
+ return 0;
}
ret = btr_exec_up(btrn, "sample_rate", &buf);
if (ret < 0) {
btr_add_output(header, WAV_HEADER_LEN, btrn);
ret = -E_WAV_SUCCESS;
err:
- t->error = ret;
if (ret == -E_WAV_SUCCESS)
btr_splice_out_node(btrn);
else {
btr_remove_node(&fn->btrn);
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
}
+ return ret;
}
/**
<h1>Download</h1>
<hr>
-<p> Clone the git repository by executing </p>
+Paraslash is only available as source code, no binary packages are
+provided at this point. There are several ways to download the source:
-<p> <b> git clone git://paraslash.systemlinux.org/git paraslash </b> </p>
+<ul>
+ <li> <em> git</em>.
-<p> Or grab the <a href="releases/paraslash-git.tar.bz2">tarball</a>
-of the current master branch, or download the latest version from the
-<a href="releases/">download directory</a>. All regular releases are
-<a href="PUBLIC_KEY">cryptographically signed</a>. Since development
-takes place in separate topic branches the master branch is expected
-to be more stable than any of the released versions. </p>
+ Clone the git repository by executing
+
+ <p> <pre> <kbd> git clone git://paraslash.systemlinux.org/git paraslash </kbd> </pre> </p>
+
+ <p> The repository contains the full history of the
+ project since 2006, all work in progress and the source
+ code for the web pages. Choosing this option allows to
+ check out any of the four integration branches maint,
+ master, next, pu (see the
+
+ <a href="manual.html#git_branches">git_branches</a>
+
+ section of the manual). All previous releases
+ correspond to tagged commits and may be checked out
+ as well. Since development takes place in separate
+ topic branches the master branch is expected to be
+ more stable than any of the released versions. </p>
+
+ <p> Compiling from git requires additional tools,
+ notably the autoconf package must be installed. </p>
+
+ </li>
+
+ <li> <em> regular releases</em>.
+
+ All released versions can be downloaded as compressed
+ tarballs from the
+
+ <a href="releases/">download directory</a>.
+
+ These tarballs are
+
+ <a href="PUBLIC_KEY">cryptographically signed</a>
+
+ and contain a pre-generated configure script. They
+ can be compiled and installed without autoconf.
+
+ </li>
+
+ <li> <em>master tarballs</em>.
+
+ Whenever significant changes are incorporated a
+
+ <a href="releases/paraslash-git.tar.bz2">tarball</a>
+
+ of the current master branch is created. All changes in
+ this tarball will be included in the next release. Like
+ for regular releases, a configure script is provided
+ for convenience.
+
+ </li>
+
+ <li> <em>gitweb snapshots</em>.
+
+ The
+
+ <a href="/gitweb/gitweb.cgi?p=.git;a=summary">gitweb</a>
+
+ page contains a snapshot link for each revision. This
+ allows to get a specific revision without downloading
+ the full history.
+
+ </li>
+</ul>
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>
+ <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
<title>Paraslash</title>
<LINK href="para.css" REL="stylesheet" TYPE="text/css">
<link rel="shortcut icon" href="paraslash.ico">
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>
+ <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
<title>Paraslash</title>
<LINK href="../../para.css" REL="stylesheet" TYPE="text/css">
<link rel="shortcut icon" href="../../paraslash.ico">
<h1>Events</h1>
<hr>
<ul>
+ <li>2013-07-29: <a href="releases/paraslash-0.4.13.tar.bz2">paraslash-0.4.13</a>
+ <a href="releases/paraslash-0.4.13.tar.bz2.asc">(sig)</a>
+ "spectral gravity"
+ </li>
<li>2012-12-20: <a href="releases/paraslash-0.4.12.tar.bz2">paraslash-0.4.12</a>
<a href="releases/paraslash-0.4.12.tar.bz2.asc">(sig)</a>
"volatile relativity"
*para_server*
-para_server streams binary audio data (MP3, OGG/Vorbis, OGG/Speex,
-M4A, WMA files) over local and/or remote networks. It listens on a
-TCP port and accepts commands such as play, stop, pause, next from
-authenticated clients. There are many more commands though, see the
-man page of para_server for a description of all commands.
+para_server streams binary audio data (MP3, ...) over local and/or
+remote networks. It listens on a TCP port and accepts commands such
+as play, stop, pause, next from authenticated clients. There are
+many more commands though, see the man page of para_server for a
+description of all commands.
It supports three built-in network streaming protocols
(senders/receivers): HTTP, DCCP, or UDP. This is explained in more
Requirements
~~~~~~~~~~~~
+For the impatient:
-In any case you'll need
+ git clone git://git.tuebingen.mpg.de/osl
+ cd osl && make && sudo make install && sudo ldconfig
+ sudo apt-get install autoconf libssl-dev help2man gengetopt \
+ libmad0-dev libid3tag0-dev libasound2-dev libvorbis-dev \
+ libfaad-dev libspeex-dev libFLAC-dev libsamplerate-dev \
+ libasound2-dev libao-dev libreadline-dev libncurses-dev
+
+Detailed description: In any case you'll need
- XREFERENCE(http://systemlinux.org/~maan/osl/, libosl).
The _object storage layer_ library is used by para_server. To
or decode files encoded with the _Free Lossless Audio Codec_,
libFLAC (libFLAC-dev) must be installed.
+ - XREFERENCE(http://www.mega-nerd.com/SRC/index.html,
+ libsamplerate). The resample filter will only be compiled if
+ this library is installed. Debian package: libsamplerate-dev.
+
- XREFERENCE(ftp://ftp.alsa-project.org/pub/lib/, alsa-lib). On
Linux, you'll need to have ALSA's development package
libasound2-dev installed.
libao). Needed to build the ao writer (ESD, PulseAudio,...).
Debian package: libao-dev.
+ - XREFERENCE(ftp://ftp.gnu.org/pub/gnu/ncurses, curses). Needed
+ for para_gui. Debian package: libncurses-dev.
+
- XREFERENCE(http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html,
GNU Readline). If this library (libreadline-dev) is installed,
para_client, para_audioc and para_play support interactive
};
/* wma_common.c */
-int wma_log2(unsigned int v);
+__a_const int wma_log2(unsigned int v);
const char *search_pattern(const char *pattern, int pattern_len,
const char *buf, int buf_size);
int read_asf_header(const char *buf, int loaded, struct asf_header_info *ahi);
#define _XOPEN_SOURCE 600
-#include <sys/time.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#define WMA_OUTPUT_BUFFER_SIZE (128 * 1024)
-static void wmadec_post_select(__a_unused struct sched *s, struct task *t)
+static int wmadec_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
int ret, converted, out_size;
next_buffer:
converted = 0;
- t->error = 0;
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (ret < 0)
goto err;
if (ret == 0)
- return;
+ return 0;
btr_merge(btrn, fn->min_iqs);
len = btr_next_buffer(btrn, (char **)&in);
ret = -E_WMADEC_EOF;
converted += ret + WMA_FRAME_SKIP;
success:
btr_consume(btrn, converted);
- return;
+ return 0;
err:
assert(ret < 0);
- t->error = ret;
btr_remove_node(&fn->btrn);
+ return ret;
}
static void wmadec_open(struct filter_node *fn)
__noreturn static void print_help_and_die(void)
{
- int d = conf.detailed_help_given;
- const char **p = d? write_args_info_detailed_help
- : write_args_info_help;
-
- printf_or_die("%s\n\n", WRITE_CMDLINE_PARSER_PACKAGE "-"
- WRITE_CMDLINE_PARSER_VERSION);
- printf_or_die("%s\n\n", write_args_info_usage);
- for (; *p; p++)
- printf_or_die("%s\n", *p);
- print_writer_helps(d);
+ struct ggo_help h = DEFINE_GGO_HELP(write);
+ bool d = conf.detailed_help_given;
+
+ ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+ print_writer_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
exit(0);
}
check_wav_pre_select(s, wt->cwc);
}
-static void write_post_select(__a_unused struct sched *s, struct task *t)
+static int write_post_select(__a_unused struct sched *s, struct task *t)
{
struct write_task *wt = container_of(t, struct write_task, task);
- t->error = check_wav_post_select(wt->cwc);
+ return check_wav_post_select(wt->cwc);
}
static int setup_and_schedule(void)
},
};
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
sit.btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = "stdin"));
stdin_set_defaults(&sit);
{
int ret;
- writer_init();
write_cmdline_parser(argc, argv, &conf);
- HANDLE_VERSION_FLAG("write", conf);
+ loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ writer_init();
+ version_handle_flag("write", conf.version_given);
if (conf.help_given || conf.detailed_help_given)
print_help_and_die();
*
* Called from the post_select function of the writer node's task.
*/
- void (*post_select)(struct sched *s, struct task *t);
+ int (*post_select)(struct sched *s, struct task *t);
/**
* Close one instance of the writer.
*
.handler = w->execute, .context = wn));
strcpy(wn->task.status, name);
free(name);
- wn->task.post_select = w->post_select;
wn->task.pre_select = w->pre_select;
+ wn->task.post_select = w->post_select;
register_task(s, &wn->task);
}
/**
* Print the help text of all writers to stdout.
*
- * \param detailed Whether to print the detailed help text.
+ * \param flags Passed to \ref ggo_print_help().
*/
-void print_writer_helps(int detailed)
+void print_writer_helps(unsigned flags)
{
int i;
- printf_or_die("\nAvailable writers: \n\t");
+ printf_or_die("\nAvailable writers: ");
FOR_EACH_WRITER(i)
printf_or_die("%s%s", i? " " : "", writer_names[i]);
- printf_or_die("\n\n");
+ printf_or_die("\n");
FOR_EACH_WRITER(i) {
struct writer *w = writers + i;
if (!w->help.short_help)
continue;
- printf_or_die("Options for %s:\n", writer_names[i]);
- ggo_print_help(&w->help, detailed);
+ printf_or_die("\n%s: %s", writer_names[i],
+ w->help.purpose);
+ ggo_print_help(&w->help, flags);
}
}
void writer_init(void);
void *check_writer_arg_or_die(const char *wa, int *writer_num);
-void print_writer_helps(int detailed);
+void print_writer_helps(unsigned flags);
void register_writer_node(struct writer_node *wn, struct btr_node *parent,
struct sched *s);
void get_btr_sample_rate(struct btr_node *btrn, int32_t *result);