--arg-struct-name=$(subst .ggo,,$<)_args_info \
--file-name=$(subst .ggo,,$<).cmdline \
--func-name $(subst _filter.ggo,,$<)_cmdline_parser < $<
+%_write.cmdline.h %_write.cmdline.c: %_write.ggo
+ gengetopt -S $(module_ggo_opts) \
+ --set-package=$(subst .ggo,,$<) \
+ --arg-struct-name=$(subst .ggo,,$<)_args_info \
+ --file-name=$(subst .ggo,,$<).cmdline \
+ --func-name $(subst _write.ggo,,$<)_cmdline_parser < $<
%.cmdline.h %.cmdline.c: %.ggo
case $< in client.ggo) O="--unamed-opts=command";; \
--- /dev/null
+section "alsa options"
+######################
+
+option "device" d
+#~~~~~~~~~~~~~~~~
+"set PCM device"
+ string typestr="device"
+ default="plughw:0,0"
+ optional
+
+option "channels" c
+#~~~~~~~~~~~~~~~~~~
+"number of channels (only neccessary for raw
+audio)"
+
+ int typestr="num"
+ default="2"
+ optional
+
+option "sample_rate" s
+#~~~~~~~~~~~~~~~~~~~~~
+
+"force given sample rate (only neccessary for
+raw audio)"
+
+ int typestr="num"
+ default="44100"
+ optional
#include <alsa/asoundlib.h>
-#include "write.cmdline.h"
+#include "alsa_write.cmdline.h"
#include "error.h"
-extern struct gengetopt_args_info conf;
#define FORMAT SND_PCM_FORMAT_S16_LE
/** data specific to the alsa writer */
struct private_alsa_data {
-/** the alsa handle */
-snd_pcm_t *handle;
-/** determined and set by alsa_open() */
-size_t bytes_per_frame;
-struct timeval next_chunk;
+ /** the alsa handle */
+ snd_pcm_t *handle;
+ /** determined and set by alsa_open() */
+ size_t bytes_per_frame;
+ /** don't write anything until this time */
+ struct timeval next_chunk;
};
/*
snd_pcm_info_t *info;
snd_pcm_uframes_t period_size;
struct private_alsa_data *pad = para_calloc(sizeof(struct private_alsa_data));
- w->private_data = pad;
+ struct alsa_write_args_info *conf = w->conf;
+ w->private_data = pad;
snd_pcm_info_alloca(&info);
- err = snd_pcm_open(&pad->handle, conf.device_arg,
+ err = snd_pcm_open(&pad->handle, conf->device_arg,
SND_PCM_STREAM_PLAYBACK, 0);
if (err < 0)
return -E_PCM_OPEN;
if (snd_pcm_hw_params_set_format(pad->handle, hwparams, FORMAT) < 0)
return -E_SAMPLE_FORMAT;
if (snd_pcm_hw_params_set_channels(pad->handle, hwparams,
- conf.channels_arg) < 0)
+ conf->channels_arg) < 0)
return -E_CHANNEL_COUNT;
if (snd_pcm_hw_params_set_rate_near(pad->handle, hwparams,
- (unsigned int*) &conf.sample_rate_arg, 0) < 0)
+ (unsigned int*) &conf->sample_rate_arg, 0) < 0)
return -E_SET_RATE;
err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0);
if (err < 0 || !buffer_time)
if (snd_pcm_sw_params(pad->handle, swparams) < 0)
return -E_SW_PARAMS;
pad->bytes_per_frame = snd_pcm_format_physical_width(FORMAT)
- * conf.channels_arg / 8;
+ * conf->channels_arg / 8;
// if (snd_pcm_nonblock(pad->handle, 1))
// PARA_ERROR_LOG("%s\n", "failed to set nonblock mode");
return period_size * pad->bytes_per_frame;
free(pad);
}
+__malloc void *alsa_parse_config(char *options)
+{
+ struct alsa_write_args_info *conf
+ = para_calloc(sizeof(struct alsa_write_args_info));
+ PARA_INFO_LOG("options: %s, %d\n", options, strcspn(options, " \t"));
+ int ret = alsa_cmdline_parser_string(options, conf, "alsa_write");
+ if (ret)
+ goto err_out;
+ PARA_INFO_LOG("help given: %d\n", conf->help_given);
+ return conf;
+err_out:
+ free(conf);
+ return NULL;
+}
+
/** the init function of the alsa writer */
void alsa_writer_init(struct writer *w)
{
w->close = alsa_close;
w->pre_select = alsa_write_pre_select;
w->post_select = alsa_write_post_select;
+ w->parse_config = alsa_parse_config;
w->shutdown = NULL; /* nothing to do */
}
register_task(&stat_task->task);
register_task(&at->task);
s.default_timeout.tv_sec = 0;
- s.default_timeout.tv_usec = 999 * 1000;
+ s.default_timeout.tv_usec = 99 * 1000;
ret = sched(&s);
PARA_EMERG_LOG("%s\n", PARA_STRERROR(-ret));
ipc dccp dccp_send fd"
server_ldflags=""
-write_cmdline_objs="write.cmdline"
+write_cmdline_objs="write.cmdline file_write.cmdline"
write_errlist_objs="write write_common file_writer time fd string sched stdin"
write_ldflags=""
write_writers="file"
])
if test "$have_alsa" = "yes"; then
write_errlist_objs="$write_errlist_objs alsa_writer"
+ write_cmdline_objs="$write_cmdline_objs alsa_write.cmdline"
write_ldflags="$write_ldflags -lasound"
write_writers="$write_writers alsa"
fi
--- /dev/null
+section "file writer options"
+
+option "filename" f
+#~~~~~~~~~~~~~~~~~~
+
+"select output file name. Defaults to a
+random filename in ~/.paraslash."
+
+ string typestr="filename"
+ optional
+
#include "write.h"
#include "string.h"
#include "fd.h"
+#include "file_write.cmdline.h"
#include "error.h"
/** data specific to the file writer */
struct private_file_writer_data {
-/** the file descriptor of the output file */
-int fd;
-int check_fd;
+ /** the file descriptor of the output file */
+ int fd;
+ /** non-zero if \a fd was added to the write fd set */
+ int check_fd;
};
static int file_writer_open(struct writer_node *wn)
{
struct private_file_writer_data *pfwd = para_calloc(
sizeof(struct private_file_writer_data));
- char *tmp = para_tmpname(), *home = para_homedir(),
- *filename = make_message("%s/.paraslash/%s", home, tmp);
-
- free(home);
- free(tmp);
+ struct file_write_args_info *conf = wn->conf;
+ char *filename;
+ if (conf->filename_given)
+ filename = conf->filename_arg;
+ else {
+ char *tmp = para_tmpname(), *home = para_homedir();
+ filename = make_message("%s/.paraslash/%s", home, tmp);
+ free(home);
+ free(tmp);
+ }
wn->private_data = pfwd;
pfwd->fd = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
- free(filename);
+ if (!conf->filename_given)
+ free(filename);
if (pfwd->fd >= 0)
return 8192;
free(pfwd);
free(pfwd);
}
+__malloc void *file_writer_parse_config(char *options)
+{
+ PARA_INFO_LOG("options: %s\n", options);
+ struct file_write_args_info *conf
+ = para_calloc(sizeof(struct file_write_args_info));
+ int ret = file_cmdline_parser_string(options, conf, "file_write");
+ PARA_INFO_LOG("conf->filename_given: %d\n", conf->filename_given);
+ if (!ret)
+ return conf;
+ free(conf);
+ return NULL;
+}
+
/** the init function of the file writer */
void file_writer_init(struct writer *w)
{
w->write = file_writer_write;
w->pre_select = file_writer_pre_select;
w->post_select = file_writer_post_select;
+ w->parse_config = file_writer_parse_config;
w->close = file_writer_close;
w->shutdown = NULL; /* nothing to do */
}
struct task task;
};
-struct gengetopt_args_info conf;
+static struct gengetopt_args_info conf;
struct stdin_task sit;
struct check_wav_task cwt;
struct initial_delay_task idt;
t->ret = *cwt->eof? -E_PREMATURE_END : 1;
return;
}
+ cwt->channels = 2;
+ cwt->sample_rate = 44100;
a = (unsigned char*)cwt->buf;
t->ret = -E_NO_WAV_HEADER;
if (a[0] != 'R' || a[1] != 'I' || a[2] != 'F' || a[3] != 'F')
free(msg);
exit(EXIT_SUCCESS);
}
- if (conf.prebuffer_arg < 0 || conf.prebuffer_arg > 100)
- goto out;
+// if (conf.prebuffer_arg < 0 || conf.prebuffer_arg > 100)
+// goto out;
if (conf.start_time_given) {
long unsigned sec, usec;
if (sscanf(conf.start_time_arg, "%lu:%lu",
goto out;
}
wng = wng_new(conf.writer_given);
+ ret = -E_WRITE_SYNTAX;
for (i = 0; i < conf.writer_given; i++) {
- ret = check_writer_arg(conf.writer_arg[i]);
- if (ret < 0)
+ int writer_num;
+ wng->writer_nodes[i].conf = check_writer_arg(
+ conf.writer_arg[i], &writer_num);
+ if (!wng->writer_nodes[i].conf)
goto out;
- wng->writer_nodes[i].writer = &writers[ret];
+ wng->writer_nodes[i].writer = &writers[writer_num];
sprintf(wng->writer_nodes[i].task.status, "%s",
- writer_names[ret]);
+ writer_names[writer_num]);
}
ret = 1;
out:
}
PARA_INFO_LOG("%s\n", PARA_STRERROR(-t->ret));
unregister_task(t);
- if (t->ret == -E_WAV_HEADER_SUCCESS) {
- conf.channels_arg = cwt.channels;
- conf.sample_rate_arg = cwt.sample_rate;
- }
+// if (t->ret == -E_WAV_HEADER_SUCCESS) {
+// conf.channels_arg = cwt.channels;
+// conf.sample_rate_arg = cwt.sample_rate;
+// }
idt.task.pre_select = initial_delay_pre_select;
idt.task.private_data = &idt;
idt.task.event_handler = idt_event_handler;
struct sched s;
cmdline_parser(argc, argv, &conf);
- wng = check_args();
- if (!wng)
- goto out;
init_supported_writers();
init_sched();
+ wng = check_args();
+ if (!wng)
+ goto out;
stdin_set_defaults(&sit);
+ if (conf.bufsize_given)
+ sit.bufsize = conf.bufsize_arg;
sit.buf = para_malloc(sit.bufsize),
register_task(&sit.task);
default="64"
optional
-option "prebuffer" p
-#~~~~~~~~~~~~~~~~~~~
-"delay playback until buffer is filled"
-
- int typestr="percent"
- default="100"
- optional
-
option "writer" w
#~~~~~~~~~~~~~~~~
string typestr="timeval"
optional
-
-
-section "alsa options"
-######################
-
-option "device" d
-#~~~~~~~~~~~~~~~~
-"set PCM device"
- string typestr="device"
- default="plughw:0,0"
- optional
-
-option "channels" c
-#~~~~~~~~~~~~~~~~~~
-"number of channels (only neccessary for raw
-audio)"
-
- int typestr="num"
- default="2"
- optional
-
-option "sample_rate" s
-#~~~~~~~~~~~~~~~~~~~~~
-
-"force given sample rate (only neccessary for
-raw audio)"
-
- int typestr="num"
- default="44100"
- optional
-
* decbribes one running instance of a writer
*/
struct writer_node {
-/** points to the writer structure associated with this node */
+ /** points to the writer structure associated with this node */
struct writer *writer;
-/** writer-specific data */
+ /** writer-specific data */
void *private_data;
-/** send that many bytes in one go */
+ /** send that many bytes in one go */
int chunk_bytes;
struct task task;
struct writer_node_group *wng;
+ /** the writer-specific configuration of this node */
+ void *conf;
};
/** describes one supported writer */
*
*/
void (*init)(struct writer *w);
+/**
+ *
+ *
+ * the command line parser of the writer
+ *
+ * It should check whether the command line options given by \a options are
+ * valid. On success, it should return a pointer to the writer-specific
+ * configuration data determined by \a options. Note that this might be called
+ * more than once with different values of \a options.
+ *
+ */
+ void * (*parse_config)(char *options);
/**
*
* open one instance of this writer
const char *writer_names[] ={WRITER_NAMES};
struct writer writers[NUM_SUPPORTED_WRITERS] = {WRITER_ARRAY};
-static void wng_post_select(struct sched *s, struct task *t)
+static void wng_post_select(__a_unused struct sched *s, struct task *t)
{
struct writer_node_group *g = t->private_data;
int i;
writers[i].init(&writers[i]);
}
-int check_writer_arg(const char *arg)
+void *check_writer_arg(char *wa, int *writer_num)
{
- int i, ret = -E_WRITE_COMMON_SYNTAX;
- char *a = para_strdup(arg), *p = strchr(a, ':');
- if (p)
- *p = '\0';
- p++;
+ int i;
+
+ PARA_INFO_LOG("checking %s\n", wa);
FOR_EACH_WRITER(i) {
- if (strcmp(writer_names[i], a))
+ const char *name = writer_names[i];
+ size_t len = strlen(name);
+ char c;
+ if (strlen(wa) < len)
+ continue;
+ if (strncmp(name, wa, len))
+ continue;
+ c = wa[len];
+ if (c && c != ' ')
continue;
- ret = i;
- goto out;
+ if (c && !writers[i].parse_config)
+ return NULL;
+ *writer_num = i;
+ return writers[i].parse_config(c? wa + len + 1 : "");
}
-out:
- free(a);
- return ret;
+ PARA_ERROR_LOG("%s", "writer not found\n");
+ return NULL;
}
struct writer_node_group *setup_default_wng(void)
wng->writer_nodes[0].writer = &writers[default_writer];
sprintf(wng->writer_nodes[0].task.status, "%s",
writer_names[default_writer]);
- PARA_INFO_LOG("using default writer: %s\n",
- writer_names[default_writer]);
+ PARA_INFO_LOG("using default writer: %s %p\n",
+ writer_names[default_writer], writers[default_writer].parse_config);
+ wng->writer_nodes[0].conf = writers[default_writer].parse_config("");
return wng;
}
struct writer_node_group *wng_new(unsigned num_writers);
void wng_destroy(struct writer_node_group *g);
void init_supported_writers(void);
-int check_writer_arg(const char *arg);
+void *check_writer_arg(char *wa, int *writer_num);
struct writer_node_group *setup_default_wng(void);