X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=alsa_write.c;h=3759306ec2215f0732bba5fcc76d18d011db41e1;hp=a211985ab94bb71406d7729793387bbabc5fc452;hb=bd10c0f46e0b7eadfb8c6d5cd45581cec253d775;hpb=452920e64da3451578bb2b1244bb982993b3a278 diff --git a/alsa_write.c b/alsa_write.c index a211985a..3759306e 100644 --- a/alsa_write.c +++ b/alsa_write.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Andre Noll + * Copyright (C) 2005-2014 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -15,7 +15,6 @@ #include #include #include -#include #include "para.h" #include "fd.h" @@ -35,17 +34,24 @@ struct private_alsa_write_data { snd_pcm_t *handle; /** Determined and set by alsa_init(). */ int bytes_per_frame; - /** - * The sample rate given by command line option or the decoder - * of the writer node group. + /* + * If the sample rate is not given at the command line and no wav + * header was detected, the btr exec mechanism is employed to query the + * ancestor buffer tree nodes for this information. In a typical setup + * the decoder passes the sample rate back to the alsa writer. + * + * \sa \ref btr_exec_up(). */ unsigned sample_rate; - snd_pcm_format_t sample_format; - /** - * The number of channels, given by command line option or the - * decoder of the writer node group. + /* + * The sample format (8/16 bit, signed/unsigned, little/big endian) is + * determined in the same way as the \a sample_rate. */ + snd_pcm_format_t sample_format; + /* The number of channels, again determined like \a sample_rate. */ unsigned channels; + /* time until buffer underrun occurs, in milliseconds */ + unsigned buffer_time; struct timeval drain_barrier; /* File descriptor for select(). */ int poll_fd; @@ -72,10 +78,10 @@ static int alsa_init(struct private_alsa_write_data *pad, snd_pcm_sw_params_t *swparams; snd_pcm_uframes_t start_threshold, stop_threshold; snd_pcm_uframes_t buffer_size, period_size; - snd_output_t *log; - unsigned buffer_time; + snd_output_t *output_log; int ret; const char *msg; + unsigned period_time; PARA_INFO_LOG("opening %s\n", conf->device_arg); msg = "unable to open pcm"; @@ -108,18 +114,21 @@ static int alsa_init(struct private_alsa_write_data *pad, &pad->sample_rate, NULL); if (ret < 0) goto fail; - msg = "unable to get buffer time"; - ret = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, - NULL); - if (ret < 0 || buffer_time == 0) - goto fail; - /* buffer at most 500 milliseconds */ - buffer_time = PARA_MIN(buffer_time, 500U * 1000U); + /* alsa wants microseconds */ + pad->buffer_time = conf->buffer_time_arg * 1000; msg = "could not set buffer time"; ret = snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams, - &buffer_time, NULL); + &pad->buffer_time, NULL); + if (ret < 0) + goto fail; + pad->buffer_time /= 1000; /* we prefer milliseconds */ + period_time = pad->buffer_time * 250; /* buffer time / 4 */ + msg = "could not set period time"; + ret = snd_pcm_hw_params_set_period_time_near(pad->handle, hwparams, + &period_time, 0); if (ret < 0) goto fail; + msg = "unable to install hw params"; ret = snd_pcm_hw_params(pad->handle, hwparams); if (ret < 0) @@ -163,21 +172,23 @@ static int alsa_init(struct private_alsa_write_data *pad, ret = snd_pcm_nonblock(pad->handle, 1); if (ret < 0) goto fail; - ret = snd_output_buffer_open(&log); + ret = snd_output_buffer_open(&output_log); if (ret == 0) { - char *buf; - PARA_INFO_LOG("dumping alsa configuration\n"); - snd_pcm_dump(pad->handle, log); - snd_output_buffer_string(log, &buf); - for (;;) { - char *p = strchr(buf, '\n'); - if (!p) /* omit last output line, it's empty */ + char *buf, *p; + size_t sz; + PARA_DEBUG_LOG("dumping alsa configuration\n"); + snd_pcm_dump(pad->handle, output_log); + snd_pcm_hw_params_dump(hwparams, output_log); + sz = snd_output_buffer_string(output_log, &buf); + for (p = buf; p < buf + sz;) { + char *q = memchr(p, '\n', buf + sz - p); + if (!q) break; - *p = '\0'; - PARA_INFO_LOG("%s\n", buf); - buf = p + 1; + *q = '\0'; + PARA_DEBUG_LOG("%s\n", p); + p = q + 1; } - snd_output_close(log); + snd_output_close(output_log); } return 1; fail: @@ -207,11 +218,12 @@ static void alsa_write_pre_select(struct sched *s, struct task *t) sched_request_barrier_or_min_delay(&pad->drain_barrier, s); return; } + /* wait at most 50% of the buffer time */ + sched_request_timeout_ms(pad->buffer_time / 2, s); ret = snd_pcm_poll_descriptors(pad->handle, &pfd, 1); if (ret < 0) { PARA_ERROR_LOG("could not get alsa poll fd: %s\n", snd_strerror(-ret)); - t->error = -E_ALSA; return; } pad->poll_fd = pfd.fd; @@ -236,7 +248,7 @@ static void alsa_close(struct writer_node *wn) 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); @@ -247,11 +259,13 @@ static void alsa_write_post_select(__a_unused struct sched *s, snd_pcm_sframes_t frames; int ret; + ret = task_get_notification(t); + if (ret < 0) + goto err; again: - t->error = 0; 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 */ @@ -264,17 +278,17 @@ again: 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; @@ -294,22 +308,14 @@ again: wn->min_iqs = pad->bytes_per_frame; goto again; } - if (pad->poll_fd < 0 || !FD_ISSET(pad->poll_fd, &s->rfds)) - return; frames = bytes / pad->bytes_per_frame; frames = snd_pcm_writei(pad->handle, data, frames); if (frames == 0 || frames == -EAGAIN) { - /* - * The alsa poll fd was ready for IO but we failed to write to - * the alsa handle. This means another event is pending. We - * don't care about that but we have to dispatch the event in - * order to avoid a busy loop. So we simply read from the poll - * fd. - */ char buf[100]; - if (read(pad->poll_fd, buf, 100)) - do_nothing; - return; + if (pad->poll_fd >= 0 && FD_ISSET(pad->poll_fd, &s->rfds)) + if (read(pad->poll_fd, buf, 100)) + do_nothing; + return 0; } if (frames > 0) { btr_consume(btrn, frames * pad->bytes_per_frame); @@ -318,28 +324,28 @@ again: 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(btrn); - t->error = ret; + btr_remove_node(&wn->btrn); + return ret; } -__malloc static void *alsa_parse_config_or_die(const char *options) +__malloc static void *alsa_parse_config_or_die(int argc, char **argv) { struct alsa_write_args_info *conf = para_calloc(sizeof(*conf)); /* exits on errors */ - alsa_cmdline_parser_string(options, conf, "alsa_write"); + alsa_write_cmdline_parser(argc, argv, conf); return conf; } static void alsa_free_config(void *conf) { - alsa_cmdline_parser_free(conf); + alsa_write_cmdline_parser_free(conf); } /** @@ -353,16 +359,12 @@ void alsa_write_init(struct writer *w) { struct alsa_write_args_info dummy; - alsa_cmdline_parser_init(&dummy); + alsa_write_cmdline_parser_init(&dummy); w->close = alsa_close; w->pre_select = alsa_write_pre_select; w->post_select = alsa_write_post_select; w->parse_config_or_die = alsa_parse_config_or_die; - w->shutdown = NULL; /* nothing to do */ 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 - }; - alsa_cmdline_parser_free(&dummy); + w->help = (struct ggo_help)DEFINE_GGO_HELP(alsa_write); + alsa_write_cmdline_parser_free(&dummy); }