paraslash 0.7.3
[paraslash.git] / alsa_write.c
index 3759306ec2215f0732bba5fcc76d18d011db41e1..53d7b1b454932c36b3e578d794d688cee6cf7270 100644 (file)
@@ -1,8 +1,4 @@
-/*
- * Copyright (C) 2005-2014 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
+/* Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
 
 /** \file alsa_write.c paraslash's alsa output plugin */
 
 #include <regex.h>
 #include <sys/types.h>
 #include <alsa/asoundlib.h>
+#include <lopsub.h>
 
+#include "write_cmd.lsg.h"
 #include "para.h"
 #include "fd.h"
 #include "string.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "write.h"
-#include "write_common.h"
-#include "alsa_write.cmdline.h"
 #include "error.h"
 
 /** Data specific to the alsa writer. */
@@ -53,7 +48,7 @@ struct private_alsa_write_data {
        /* time until buffer underrun occurs, in milliseconds */
        unsigned buffer_time;
        struct timeval drain_barrier;
-       /* File descriptor for select(). */
+       /* File descriptor to monitor for reading. */
        int poll_fd;
 };
 
@@ -71,25 +66,26 @@ static snd_pcm_format_t get_alsa_pcm_format(enum sample_format sf)
 }
 
 /* Install PCM software and hardware configuration. */
-static int alsa_init(struct private_alsa_write_data *pad,
-               struct alsa_write_args_info *conf)
+static int alsa_init(struct writer_node *wn)
 {
-       snd_pcm_hw_params_t *hwparams;
-       snd_pcm_sw_params_t *swparams;
+       struct private_alsa_write_data *pad = wn->private_data;
+       snd_pcm_hw_params_t *hwparams = NULL;
+       snd_pcm_sw_params_t *swparams = NULL;
        snd_pcm_uframes_t start_threshold, stop_threshold;
        snd_pcm_uframes_t buffer_size, period_size;
        snd_output_t *output_log;
        int ret;
-       const char *msg;
+       const char *msg, *dev = WRITE_CMD_OPT_STRING_VAL(ALSA, DEVICE, wn->lpr);
        unsigned period_time;
 
-       PARA_INFO_LOG("opening %s\n", conf->device_arg);
+       PARA_INFO_LOG("opening %s\n", dev);
        msg = "unable to open pcm";
-       ret = snd_pcm_open(&pad->handle, conf->device_arg,
-               SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+       ret = snd_pcm_open(&pad->handle, dev, SND_PCM_STREAM_PLAYBACK,
+               SND_PCM_NONBLOCK);
        if (ret < 0)
                goto fail;
-       snd_pcm_hw_params_alloca(&hwparams);
+       ret = snd_pcm_hw_params_malloc(&hwparams);
+       assert(ret >= 0);
        msg = "Broken alsa configuration";
        ret = snd_pcm_hw_params_any(pad->handle, hwparams);
        if (ret < 0)
@@ -115,7 +111,8 @@ static int alsa_init(struct private_alsa_write_data *pad,
        if (ret < 0)
                goto fail;
        /* alsa wants microseconds */
-       pad->buffer_time = conf->buffer_time_arg * 1000;
+       pad->buffer_time = 1000U * WRITE_CMD_OPT_UINT32_VAL(ALSA, BUFFER_TIME,
+               wn->lpr);
        msg = "could not set buffer time";
        ret = snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams,
                &pad->buffer_time, NULL);
@@ -125,7 +122,7 @@ static int alsa_init(struct private_alsa_write_data *pad,
        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);
+               &period_time, NULL);
        if (ret < 0)
                goto fail;
 
@@ -140,7 +137,8 @@ static int alsa_init(struct private_alsa_write_data *pad,
                goto fail;
 
        /* software parameter setup */
-       snd_pcm_sw_params_alloca(&swparams);
+       ret = snd_pcm_sw_params_malloc(&swparams);
+       assert(ret >= 0);
        snd_pcm_sw_params_current(pad->handle, swparams);
        snd_pcm_sw_params_set_avail_min(pad->handle, swparams, period_size);
        if (buffer_size < 1)
@@ -190,19 +188,24 @@ static int alsa_init(struct private_alsa_write_data *pad,
                }
                snd_output_close(output_log);
        }
-       return 1;
+       ret = 1;
+       goto out;
 fail:
        if (ret < 0)
                PARA_ERROR_LOG("%s: %s\n", msg, snd_strerror(-ret));
        else
                PARA_ERROR_LOG("%s\n", msg);
-       return -E_ALSA;
+       ret = -E_ALSA;
+out:
+       snd_pcm_hw_params_free(hwparams);
+       snd_pcm_sw_params_free(swparams);
+       return ret;
 }
 
-static void alsa_write_pre_select(struct sched *s, struct task *t)
+static void alsa_write_pre_monitor(struct sched *s, void *context)
 {
        struct pollfd pfd;
-       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct writer_node *wn = context;
        struct private_alsa_write_data *pad = wn->private_data;
        int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
 
@@ -227,7 +230,7 @@ static void alsa_write_pre_select(struct sched *s, struct task *t)
                return;
        }
        pad->poll_fd = pfd.fd;
-       para_fd_set(pfd.fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(pfd.fd, s);
 }
 
 static void alsa_close(struct writer_node *wn)
@@ -237,6 +240,8 @@ static void alsa_close(struct writer_node *wn)
 
        if (!pad)
                return;
+       if (!pad->handle)
+               goto free_pad;
        /*
         * It's OK to have a blocking operation here because we already made
         * sure that the PCM output buffer is (nearly) empty.
@@ -245,13 +250,13 @@ static void alsa_close(struct writer_node *wn)
        snd_pcm_drain(pad->handle);
        snd_pcm_close(pad->handle);
        snd_config_update_free_global();
+free_pad:
        free(pad);
 }
 
-static int alsa_write_post_select(__a_unused struct sched *s,
-               struct task *t)
+static int alsa_write_post_monitor(__a_unused struct sched *s, void *context)
 {
-       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct writer_node *wn = context;
        struct private_alsa_write_data *pad = wn->private_data;
        struct btr_node *btrn = wn->btrn;
        char *data;
@@ -259,18 +264,20 @@ static int alsa_write_post_select(__a_unused struct sched *s,
        snd_pcm_sframes_t frames;
        int ret;
 
-       ret = task_get_notification(t);
+       ret = task_get_notification(wn->task);
        if (ret < 0)
                goto err;
 again:
        ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
+       if (ret < 0)
+               goto err;
        if (ret == 0)
                return 0;
        btr_merge(btrn, wn->min_iqs);
        bytes = btr_next_buffer(btrn, &data);
        if (ret < 0 || bytes < wn->min_iqs) { /* eof */
                assert(btr_no_parent(btrn));
-               ret = -E_WRITE_COMMON_EOF;
+               ret = -E_EOF;
                if (!pad)
                        goto err;
                /* wait until pending frames are played */
@@ -289,22 +296,24 @@ again:
 
                if (bytes == 0) /* no data available */
                        return 0;
-               pad = para_calloc(sizeof(*pad));
-               get_btr_sample_rate(btrn, &val);
+               pad = wn->private_data = zalloc(sizeof(*pad));
+               ret = get_btr_sample_rate(btrn, &val);
+               if (ret < 0)
+                       goto err;
                pad->sample_rate = val;
-               get_btr_channels(btrn, &val);
+               ret = get_btr_channels(btrn, &val);
+               if (ret < 0)
+                       goto err;
                pad->channels = val;
-               get_btr_sample_format(btrn, &val);
+               ret = get_btr_sample_format(btrn, &val);
+               if (ret < 0)
+                       goto err;
                pad->sample_format = get_alsa_pcm_format(val);
-
-               PARA_INFO_LOG("%d channel(s), %dHz\n", pad->channels,
+               PARA_INFO_LOG("%u channel(s), %uHz\n", pad->channels,
                        pad->sample_rate);
-               ret = alsa_init(pad, wn->conf);
-               if (ret < 0) {
-                       free(pad);
+               ret = alsa_init(wn);
+               if (ret < 0)
                        goto err;
-               }
-               wn->private_data = pad;
                wn->min_iqs = pad->bytes_per_frame;
                goto again;
        }
@@ -312,7 +321,7 @@ again:
        frames = snd_pcm_writei(pad->handle, data, frames);
        if (frames == 0 || frames == -EAGAIN) {
                char buf[100];
-               if (pad->poll_fd >= 0 && FD_ISSET(pad->poll_fd, &s->rfds))
+               if (pad->poll_fd >= 0 && sched_read_ok(pad->poll_fd, s))
                        if (read(pad->poll_fd, buf, 100))
                                do_nothing;
                return 0;
@@ -322,7 +331,11 @@ again:
                goto again;
        }
        if (frames == -EPIPE) {
-               PARA_WARNING_LOG("underrun (tried to write %zu bytes)\n", bytes);
+               snd_pcm_status_t *status;
+               snd_pcm_status_malloc(&status);
+               if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN)
+                       PARA_WARNING_LOG("underrun\n");
+               snd_pcm_status_free(status);
                snd_pcm_prepare(pad->handle);
                return 0;
        }
@@ -334,37 +347,9 @@ err:
        return ret;
 }
 
-__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_write_cmdline_parser(argc, argv, conf);
-       return conf;
-}
-
-static void alsa_free_config(void *conf)
-{
-       alsa_write_cmdline_parser_free(conf);
-}
+struct writer lsg_write_cmd_com_alsa_user_data = {
 
-/**
- * The init function of the alsa writer.
- *
- * \param w Pointer to the writer to initialize.
- *
- * \sa struct \ref writer.
- */
-void alsa_write_init(struct writer *w)
-{
-       struct alsa_write_args_info 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->free_config = alsa_free_config;
-       w->help = (struct ggo_help)DEFINE_GGO_HELP(alsa_write);
-       alsa_write_cmdline_parser_free(&dummy);
-}
+       .pre_monitor = alsa_write_pre_monitor,
+       .post_monitor = alsa_write_post_monitor,
+       .close = alsa_close,
+};