diff libao2/ao_sndio.c @ 36463:c6843e5bf166

libao2: Add sndio audio output module. Patch by Alexandre Ratchov [alex caoua org].
author reimar
date Tue, 31 Dec 2013 15:30:49 +0000
parents
children cae4a4a36016
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libao2/ao_sndio.c	Tue Dec 31 15:30:49 2013 +0000
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <sys/types.h>
+#include <poll.h>
+#include <errno.h>
+#include <sndio.h>
+#include <stdlib.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "mixer.h"
+#include "help_mp.h"
+
+#include "libaf/af_format.h"
+
+#include "audio_out.h"
+#include "audio_out_internal.h"
+
+static ao_info_t info = {
+    "sndio audio output",
+    "sndio",
+    "Alexandre Ratchov <alex@caoua.org>",
+    ""
+};
+
+LIBAO_EXTERN(sndio)
+
+static struct sio_hdl *hdl = NULL;
+struct pollfd *pfds = NULL;
+static struct sio_par par;
+static int delay, vol, havevol;
+static int prepause_delay;
+
+/*
+ * control misc parameters (only the volume for now)
+ */
+static int control(int cmd, void *arg)
+{
+    ao_control_vol_t *ctl = arg;
+
+    switch (cmd) {
+    case AOCONTROL_GET_VOLUME:
+        if (!havevol)
+            return CONTROL_FALSE;
+        ctl->left = ctl->right = vol * 100 / SIO_MAXVOL;
+        break;
+    case AOCONTROL_SET_VOLUME:
+        if (!havevol)
+            return CONTROL_FALSE;
+        sio_setvol(hdl, ctl->left * SIO_MAXVOL / 100);
+        break;
+    default:
+        return CONTROL_UNKNOWN;
+    }
+    return CONTROL_OK;
+}
+
+/*
+ * call-back invoked whenever the the hardware position changes
+ */
+static void movecb(void *addr, int delta)
+{
+    delay -= delta * (int)(par.bps * par.pchan);
+}
+
+/*
+ * call-back invoked whenever the volume changes
+ */
+static void volcb(void *addr, unsigned newvol)
+{
+    vol = newvol;
+}
+
+/*
+ * open device and setup parameters
+ * return: 1 = success, 0 = failure
+ */
+static int init(int rate, int channels, int format, int flags)
+{
+    int bpf;
+
+    hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0);
+    if (hdl == NULL) {
+        mp_msg(MSGT_AO, MSGL_ERR, "ao2: can't open sndio\n");
+        return 0;
+    }
+    sio_initpar(&par);
+    par.bits = af_fmt2bits(format);
+    par.sig = (format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_SI;
+    if (par.bits > 8)
+        par.le = (format & AF_FORMAT_END_MASK) == AF_FORMAT_LE;
+    par.rate = rate;
+    par.pchan = channels;
+    par.appbufsz = par.rate * 250 / 1000;    /* 250ms buffer */
+    par.round = par.rate * 10 / 1000;        /*  10ms block size */
+    if (!sio_setpar(hdl, &par)) {
+        mp_msg(MSGT_AO, MSGL_ERR, "ao2: couldn't set params\n");
+        goto bad_close;
+    }
+    if (!sio_getpar(hdl, &par)) {
+        mp_msg(MSGT_AO, MSGL_ERR, "ao2: couldn't get params\n");
+        goto bad_close;
+    }
+    if (par.bps != SIO_BPS(par.bits)) {
+        mp_msg(MSGT_AO, MSGL_ERR, "ao2: unsupported format\n");
+        goto bad_close;
+    }
+    pfds = calloc(sio_nfds(hdl), sizeof(*pfds));
+    if (pfds == NULL) {
+        mp_msg(MSGT_AO, MSGL_ERR, "ao2: couldn't allocate poll fds\n");
+        goto bad_close;
+    }
+    bpf = par.bps * par.pchan;
+    ao_data.format = af_bits2fmt(8 * par.bps);
+    ao_data.format |= par.sig ? AF_FORMAT_SI : AF_FORMAT_US;
+    if (par.bits > 8)
+        ao_data.format |= par.le ? AF_FORMAT_LE : AF_FORMAT_BE;
+    ao_data.channels = par.pchan;
+    ao_data.bps = bpf * par.rate;
+    ao_data.buffersize = par.bufsz * bpf;
+    ao_data.outburst = par.round * bpf;
+    ao_data.samplerate = rate;
+    havevol = sio_onvol(hdl, volcb, NULL);
+    sio_onmove(hdl, movecb, NULL);
+
+    /*
+     * prepare the device to start. It will start as soon there's enough
+     * data in the buffer to not underrun
+     */
+    delay = 0;
+    if (!sio_start(hdl)) {
+        mp_msg(MSGT_AO, MSGL_ERR, "ao2: init: couldn't start\n");
+        goto bad_free;
+    }
+    return 1;
+bad_free:
+    free(pfds);
+    pfds = NULL;
+bad_close:
+    sio_close(hdl);
+    hdl = NULL;
+    return 0;
+}
+
+/*
+ * close device
+ */
+static void uninit(int immed)
+{
+    if (hdl)
+        sio_close(hdl);
+    if (pfds)
+        free(pfds);
+}
+
+/*
+ * stop playing and prepare to restart
+ */
+static void reset(void)
+{
+    if (!sio_stop(hdl))
+        mp_msg(MSGT_AO, MSGL_ERR, "ao2: reset: couldn't stop\n");
+    delay = 0;
+    if (!sio_start(hdl))
+        mp_msg(MSGT_AO, MSGL_ERR, "ao2: reset: couldn't start\n");
+}
+
+/*
+ * refresh the "delay" counter: call sio_revents() which keeps track of
+ * the hardware position
+ */
+static void refresh(void)
+{
+    int n;
+
+    n = sio_pollfd(hdl, pfds, POLLOUT);
+    while (poll(pfds, n, 0) < 0 && errno == EINTR)
+        ; /* nothing */
+    sio_revents(hdl, pfds);
+}
+
+/*
+ * return the number of bytes available in the device buffer
+ */
+static int get_space(void)
+{
+    refresh();
+    return par.bufsz * par.pchan * par.bps - delay;
+}
+
+/*
+ * delay in seconds between first and last sample in the buffer
+ */
+static float get_delay(void)
+{
+    refresh();
+    return (float)delay / (par.bps * par.pchan * par.rate);
+}
+
+/*
+ * submit the given number of bytes to the device
+ */
+static int play(void *data, int len, int flags)
+{
+    int n;
+
+    n = sio_write(hdl, data, len);
+    delay += n;
+    return n;
+}
+
+/*
+ * pause playing
+ */
+static void audio_pause(void)
+{
+    /*
+     * sndio can't pause, so just stop
+     */
+    prepause_delay = delay;
+    if (!sio_stop(hdl))
+        mp_msg(MSGT_AO, MSGL_ERR, "ao2: pause: couldn't stop\n");
+    delay = 0;
+}
+
+/*
+ * resume playing after audio_pause()
+ */
+static void audio_resume(void)
+{
+    /*
+     * prepare to start; then refill the buffer with the number of bytes
+     * audio_pause() consumed (this will trigger start)
+     */
+    if (!sio_start(hdl))
+        mp_msg(MSGT_AO, MSGL_ERR, "ao2: resume: couldn't start\n");
+    mp_ao_resume_refill(&audio_out_sndio,
+                        par.bufsz * par.pchan * par.bps - prepause_delay);
+}