diff src/Output/esd/audio.c @ 0:13389e613d67 trunk

[svn] - initial import of audacious-plugins tree (lots to do)
author nenolod
date Mon, 18 Sep 2006 01:11:49 -0700
parents
children 7cac35581bd0
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Output/esd/audio.c	Mon Sep 18 01:11:49 2006 -0700
@@ -0,0 +1,488 @@
+/*  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  This program 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.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+#include <esd.h>
+
+#include <unistd.h>
+
+#include <libaudacious/util.h>
+
+#include "esdout.h"
+
+
+static gint fd = 0;
+static gpointer buffer;
+static gboolean going = FALSE, paused = FALSE, prebuffer, remove_prebuffer;
+static gint buffer_size, prebuffer_size, blk_size = 4096;
+static gint rd_index = 0, wr_index = 0;
+static gint output_time_offset = 0;
+static guint64 written = 0, output_bytes = 0;
+static gint bps, ebps;
+static gint flush;
+static gint format, channels, frequency, latency;
+static esd_format_t esd_format;
+static gint input_bps, input_format, input_frequency, input_channels;
+static GThread *buffer_thread;
+static gboolean realtime = FALSE;
+static void *(*esd_translate) (void *, gint);
+static int player_id_unique = 0;
+
+static gint
+get_latency(void)
+{
+    int fd, amount = 0;
+
+#ifndef HAVE_ESD_GET_LATENCY
+    esd_server_info_t *info;
+#endif
+
+    fd = esd_open_sound(esd_cfg.hostname);
+
+    if (fd == -1)
+        return 0;
+
+#ifdef HAVE_ESD_GET_LATENCY
+    amount = esd_get_latency(fd);
+#else
+    info = esd_get_server_info(fd);
+    if (info) {
+        if (info->format & ESD_STEREO) {
+            if (info->format & ESD_BITS16)
+                amount = (44100 * (ESD_BUF_SIZE + 64)) / info->rate;
+            else
+                amount = (44100 * (ESD_BUF_SIZE + 128)) / info->rate;
+        }
+        else {
+            if (info->format & ESD_BITS16)
+                amount = (2 * 44100 * (ESD_BUF_SIZE + 128)) / info->rate;
+            else
+                amount = (2 * 44100 * (ESD_BUF_SIZE + 256)) / info->rate;
+        }
+        free(info);
+    }
+    amount += ESD_BUF_SIZE * 2;
+#endif
+    esd_close(fd);
+    return amount;
+}
+
+static void *
+esd_stou8(void *data, gint length)
+{
+    int len = length;
+    unsigned char *dat = (unsigned char *) data;
+    while (len-- > 0)
+        *dat++ ^= 0x80;
+    return data;
+}
+
+static void *
+esd_utos16sw(void *data, gint length)
+{
+    int len = length;
+    short *dat = data;
+    while (len > 0) {
+        *dat = GUINT16_SWAP_LE_BE(*dat) ^ 0x8000;
+        dat++;
+        len -= 2;
+    }
+    return data;
+}
+
+static void *
+esd_utos16(void *data, gint length)
+{
+    int len = length;
+    short *dat = data;
+    while (len > 0) {
+        *dat ^= 0x8000;
+        dat++;
+        len -= 2;
+    }
+    return data;
+}
+
+static void *
+esd_16sw(void *data, gint length)
+{
+    int len = length;
+    short *dat = data;
+    while (len > 0) {
+        *dat = GUINT16_SWAP_LE_BE(*dat);
+        dat++;
+        len -= 2;
+    }
+    return data;
+}
+
+static void
+esdout_setup_format(AFormat fmt, gint rate, gint nch)
+{
+    gboolean swap_sign = FALSE;
+    gboolean swap_16 = FALSE;
+
+    format = fmt;
+    frequency = rate;
+    channels = nch;
+    switch (fmt) {
+    case FMT_S8:
+        swap_sign = TRUE;
+    case FMT_U8:
+        esd_format = ESD_BITS8;
+        break;
+    case FMT_U16_LE:
+    case FMT_U16_BE:
+    case FMT_U16_NE:
+        swap_sign = TRUE;
+    case FMT_S16_LE:
+    case FMT_S16_BE:
+    case FMT_S16_NE:
+        esd_format = ESD_BITS16;
+        break;
+    }
+
+#if (G_BYTE_ORDER == G_BIG_ENDIAN)
+    if (fmt == FMT_U16_LE || fmt == FMT_S16_LE)
+#else
+    if (fmt == FMT_U16_BE || fmt == FMT_S16_BE)
+#endif
+        swap_16 = TRUE;
+
+    esd_translate = (void *(*)()) NULL;
+    if (esd_format == ESD_BITS8) {
+        if (swap_sign == TRUE)
+            esd_translate = esd_stou8;
+    }
+    else {
+        if (swap_sign == TRUE) {
+            if (swap_16 == TRUE)
+                esd_translate = esd_utos16sw;
+            else
+                esd_translate = esd_utos16;
+        }
+        else {
+            if (swap_16 == TRUE)
+                esd_translate = esd_16sw;
+        }
+    }
+
+    bps = rate * nch;
+    if (esd_format == ESD_BITS16)
+        bps *= 2;
+    if (nch == 1)
+        esd_format |= ESD_MONO;
+    else
+        esd_format |= ESD_STEREO;
+    esd_format |= ESD_STREAM | ESD_PLAY;
+
+    latency = ((get_latency() * frequency) / 44100) * channels;
+    if (format != FMT_U8 && format != FMT_S8)
+        latency *= 2;
+}
+
+
+gint
+esdout_get_written_time(void)
+{
+    if (!going)
+        return 0;
+    return (gint) ((written * 1000) / input_bps);
+}
+
+gint
+esdout_get_output_time(void)
+{
+    guint64 bytes;
+
+    if (!fd || !going)
+        return 0;
+
+    bytes = output_bytes;
+    if (!paused)
+        bytes -= (bytes < latency ? bytes : latency);
+
+    return output_time_offset + (gint) ((bytes * 1000) / ebps);
+}
+
+gint
+esdout_used(void)
+{
+    if (realtime)
+        return 0;
+    else {
+        if (wr_index >= rd_index)
+            return wr_index - rd_index;
+        return buffer_size - (rd_index - wr_index);
+    }
+}
+
+gint
+esdout_playing(void)
+{
+    if (!going)
+        return FALSE;
+    if (!esdout_used())
+        return FALSE;
+
+    return TRUE;
+}
+
+gint
+esdout_free(void)
+{
+    if (!realtime) {
+        if (remove_prebuffer && prebuffer) {
+            prebuffer = FALSE;
+            remove_prebuffer = FALSE;
+        }
+        if (prebuffer)
+            remove_prebuffer = TRUE;
+
+        if (rd_index > wr_index)
+            return (rd_index - wr_index) - 1;
+        return (buffer_size - (wr_index - rd_index)) - 1;
+    }
+    else {
+        if (paused)
+            return 0;
+        else
+            return 1000000;
+    }
+}
+
+static void
+esdout_write_audio(gpointer data, gint length)
+{
+    AFormat new_format;
+    gint new_frequency, new_channels;
+    EffectPlugin *ep;
+
+    new_format = input_format;
+    new_frequency = input_frequency;
+    new_channels = input_channels;
+
+    ep = get_current_effect_plugin();
+    if (effects_enabled() && ep && ep->query_format) {
+        ep->query_format(&new_format, &new_frequency, &new_channels);
+    }
+
+    if (new_format != format || new_frequency != frequency
+        || new_channels != channels) {
+        output_time_offset += (gint) ((output_bytes * 1000) / ebps);
+        output_bytes = 0;
+        esdout_setup_format(new_format, new_frequency, new_channels);
+        frequency = new_frequency;
+        channels = new_channels;
+        esd_close(fd);
+        esdout_set_audio_params();
+    }
+    if (effects_enabled() && ep && ep->mod_samples)
+        length =
+            ep->mod_samples(&data, length, input_format, input_frequency,
+                            input_channels);
+    if (esd_translate)
+        output_bytes += write(fd, esd_translate(data, length), length);
+    else
+        output_bytes += write(fd, data, length);
+}
+
+
+void
+esdout_write(gpointer ptr, gint length)
+{
+    gint cnt, off = 0;
+
+    if (!realtime) {
+        remove_prebuffer = FALSE;
+
+        written += length;
+        while (length > 0) {
+            cnt = MIN(length, buffer_size - wr_index);
+            memcpy((gchar *) buffer + wr_index, (gchar *) ptr + off, cnt);
+            wr_index = (wr_index + cnt) % buffer_size;
+            length -= cnt;
+            off += cnt;
+
+        }
+    }
+    else {
+        if (paused)
+            return;
+        esdout_write_audio(ptr, length);
+        written += length;
+
+    }
+
+}
+
+void
+esdout_close(void)
+{
+    if (!going)
+        return;
+
+    going = 0;
+
+    if (!realtime)
+        g_thread_join(buffer_thread);
+    else
+        esd_close(fd);
+
+    wr_index = 0;
+    rd_index = 0;
+    g_free(esd_cfg.playername);
+    esd_cfg.playername = NULL;
+}
+
+void
+esdout_flush(gint time)
+{
+    if (!realtime) {
+        flush = time;
+        while (flush != -1)
+            g_usleep(10000);
+    }
+    else {
+        output_time_offset = time;
+        written = (guint64) (time / 10) * (guint64) (input_bps / 100);
+        output_bytes = 0;
+    }
+}
+
+void
+esdout_pause(short p)
+{
+    paused = p;
+}
+
+gpointer
+esdout_loop(gpointer arg)
+{
+    gint length, cnt;
+
+
+    while (going) {
+        if (esdout_used() > prebuffer_size)
+            prebuffer = FALSE;
+        if (esdout_used() > 0 && !paused && !prebuffer) {
+            length = MIN(blk_size, esdout_used());
+            while (length > 0) {
+                cnt = MIN(length, buffer_size - rd_index);
+                esdout_write_audio((gchar *) buffer + rd_index, cnt);
+                rd_index = (rd_index + cnt) % buffer_size;
+                length -= cnt;
+            }
+        }
+        else
+            g_usleep(10000);
+
+        if (flush != -1) {
+            output_time_offset = flush;
+            written = (guint64) (flush / 10) * (guint64) (input_bps / 100);
+            rd_index = wr_index = output_bytes = 0;
+            flush = -1;
+            prebuffer = TRUE;
+        }
+
+    }
+
+    esd_close(fd);
+    g_free(buffer);
+    return NULL;
+}
+
+void
+esdout_set_audio_params(void)
+{
+    fd = esd_play_stream(esd_format, frequency,
+                         esd_cfg.hostname, esd_cfg.playername);
+    /* Set the stream's mixer */
+    if (fd != -1)
+        esdout_mixer_init();
+    ebps = frequency * channels;
+    if (format == FMT_U16_BE || format == FMT_U16_LE ||
+        format == FMT_S16_BE || format == FMT_S16_LE ||
+        format == FMT_S16_NE || format == FMT_U16_NE)
+        ebps *= 2;
+}
+
+gint
+esdout_open(AFormat fmt, gint rate, gint nch)
+{
+    esdout_setup_format(fmt, rate, nch);
+
+    input_format = format;
+    input_channels = channels;
+    input_frequency = frequency;
+    input_bps = bps;
+
+    realtime = xmms_check_realtime_priority();
+
+    if (!realtime) {
+        buffer_size = (esd_cfg.buffer_size * input_bps) / 1000;
+        if (buffer_size < 8192)
+            buffer_size = 8192;
+        prebuffer_size = (buffer_size * esd_cfg.prebuffer) / 100;
+        if (buffer_size - prebuffer_size < 4096)
+            prebuffer_size = buffer_size - 4096;
+
+        buffer = g_malloc0(buffer_size);
+    }
+    flush = -1;
+    prebuffer = 1;
+    wr_index = rd_index = output_time_offset = written = output_bytes = 0;
+    paused = FALSE;
+    remove_prebuffer = FALSE;
+
+    esd_cfg.playername = g_strdup_printf("xmms - plugin (%d) [%d]", getpid(), player_id_unique++);
+
+    if (esd_cfg.hostname)
+        g_free(esd_cfg.hostname);
+    if (esd_cfg.use_remote)
+        esd_cfg.hostname =
+            g_strdup_printf("%s:%d", esd_cfg.server, esd_cfg.port);
+    else
+        esd_cfg.hostname = NULL;
+
+    esdout_set_audio_params();
+    if (fd == -1) {
+        g_free(esd_cfg.playername);
+        esd_cfg.playername = NULL;
+        g_free(buffer);
+        return 0;
+    }
+    going = 1;
+
+    if (!realtime)
+        buffer_thread = g_thread_create(esdout_loop, NULL, TRUE, NULL);
+    return 1;
+}
+
+void
+esdout_tell(AFormat * fmt, gint * rate, gint * nch)
+{
+	(*fmt) = format;
+	(*rate) = frequency;
+	(*nch) = channels;
+}