summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2004-08-23 18:51:41 +0000
committerLennart Poettering <lennart@poettering.net>2004-08-23 18:51:41 +0000
commitaece210569f06a9b0936bdfc839951176107a558 (patch)
tree3ce04cd54f347537c07b66cf944123c63b6dd76b
parentba902ff5f32f4d9995003e0a02f042066fc1e0a6 (diff)
initial commit
git-svn-id: file:///home/lennart/svn/public/xmms-pulse/trunk@3 ef929aba-56e2-0310-84e0-b7573d389508
-rw-r--r--src/audio.c486
1 files changed, 486 insertions, 0 deletions
diff --git a/src/audio.c b/src/audio.c
new file mode 100644
index 0000000..b5cf8b8
--- /dev/null
+++ b/src/audio.c
@@ -0,0 +1,486 @@
+#include "polyp.h"
+#include "config.h"
+
+#define PACKAGE_VERSION "0.1"
+
+static struct pa_stream *stream = NULL;
+static struct pa_context *context = NULL;
+static struct pa_glib_mainloop *glib_mainloop = NULL;
+static struct pa_mainloop_api *mainloop = NULL;
+
+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 polyp_format_t polyp_format;
+static gint input_bps, input_format, input_frequency, input_channels;
+static pthread_t buffer_thread;
+static gboolean realtime = FALSE;
+static void *(*polyp_translate)(void *, gint);
+
+static gint get_latency(void) {
+}
+
+static void polyp_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:
+ polyp_format = POLYP_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:
+ polyp_format = POLYP_BITS16;
+ break;
+ }
+
+#ifdef WORDS_BIGENDIAN
+ if (fmt == FMT_U16_LE || fmt == FMT_S16_LE)
+#else
+ if (fmt == FMT_U16_BE || fmt == FMT_S16_BE)
+#endif
+ swap_16 = TRUE;
+
+ polyp_translate = (void*(*)())NULL;
+ if (polyp_format == POLYP_BITS8) {
+ if (swap_sign == TRUE)
+ polyp_translate = polyp_stou8;
+ } else {
+ if (swap_sign == TRUE) {
+ if (swap_16 == TRUE)
+ polyp_translate = polyp_utos16sw;
+ else
+ polyp_translate = polyp_utos16;
+ } else {
+ if (swap_16 == TRUE)
+ polyp_translate = polyp_16sw;
+ }
+ }
+
+ bps = rate * nch;
+ if (polyp_format == POLYP_BITS16)
+ bps *= 2;
+ if(nch == 1)
+ polyp_format |= POLYP_MONO;
+ else
+ polyp_format |= POLYP_STEREO;
+ polyp_format |= POLYP_STREAM | POLYP_PLAY;
+
+ latency = ((get_latency() * frequency) / 44100) * channels;
+ if (format != FMT_U8 && format != FMT_S8)
+ latency *= 2;
+}
+
+
+gint polyp_get_written_time(void)
+{
+ if (!going)
+ return 0;
+ return (gint) ((written * 1000) / input_bps);
+}
+
+gint polyp_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 + (bytes * 1000) / ebps;
+}
+
+gint polyp_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 polyp_playing(void)
+{
+ if (!going)
+ return FALSE;
+ if (!polyp_used())
+ return FALSE;
+
+ return TRUE;
+}
+
+gint polyp_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 polyp_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 += (output_bytes * 1000) / ebps;
+ output_bytes = 0;
+ polyp_setup_format(new_format, new_frequency, new_channels);
+ frequency = new_frequency;
+ channels = new_channels;
+ polyp_close(fd);
+ polyp_set_audio_params();
+ }
+ if(effects_enabled() && ep && ep->mod_samples)
+ length = ep->mod_samples(&data,length, input_format, input_frequency, input_channels);
+ if (polyp_translate)
+ output_bytes += write(fd,polyp_translate(data,length),length);
+ else
+ output_bytes += write(fd,data,length);
+}
+
+
+void polyp_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;
+ polyp_write_audio(ptr,length);
+ written += length;
+
+ }
+
+}
+
+void polyp_close(void)
+{
+ if (!going)
+ return;
+
+ going = 0;
+
+ if (!realtime)
+ pthread_join(buffer_thread, NULL);
+ else
+ polyp_close(fd);
+
+ wr_index = 0;
+ rd_index = 0;
+ g_free(polyp_cfg.playername);
+ polyp_cfg.playername = NULL;
+ polyp_reset_playerid();
+}
+
+void polyp_flush(gint time)
+{
+ if (!realtime)
+ {
+ flush = time;
+ while (flush != -1)
+ xmms_usleep(10000);
+ }
+ else
+ {
+ output_time_offset = time;
+ written = (guint64)(time / 10) * (guint64)(input_bps / 100);
+ output_bytes = 0;
+ }
+}
+
+void polyp_pause(short p)
+{
+ paused = p;
+}
+
+void *polyp_loop(void *arg)
+{
+ gint length, cnt;
+
+
+ while (going)
+ {
+ if (polyp_used() > prebuffer_size)
+ prebuffer = FALSE;
+ if (polyp_used() > 0 && !paused && !prebuffer)
+ {
+ length = MIN(blk_size, polyp_used());
+ while (length > 0)
+ {
+ cnt = MIN(length,buffer_size-rd_index);
+ polyp_write_audio((gchar *)buffer + rd_index, cnt);
+ rd_index=(rd_index+cnt)%buffer_size;
+ length-=cnt;
+ }
+ }
+ else
+ xmms_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;
+ }
+
+ }
+
+ polyp_close(fd);
+ g_free(buffer);
+ pthread_exit(NULL);
+}
+
+void polyp_set_audio_params(void)
+{
+ fd = polyp_play_stream(polyp_format, frequency,
+ polyp_cfg.hostname, polyp_cfg.playername);
+ /* Set the stream's mixer */
+ if (fd != -1)
+ polyp_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;
+}
+
+int polyp_open(AFormat fmt, int rate, int nch) {
+ static unsigned int playercnt = 0;
+
+ polyp_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 = (polyp_cfg.buffer_size * input_bps) / 1000;
+ if (buffer_size < 8192)
+ buffer_size = 8192;
+ prebuffer_size = (buffer_size * polyp_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;
+
+ polyp_cfg.playername = g_strdup_printf("xmms - plugin (%d-%u)",
+ getpid(), playercnt++);
+
+ if (polyp_cfg.hostname)
+ g_free(polyp_cfg.hostname);
+ if (polyp_cfg.use_remote)
+ polyp_cfg.hostname = g_strdup_printf("%s:%d", polyp_cfg.server, polyp_cfg.port);
+ else
+ polyp_cfg.hostname = NULL;
+
+ polyp_set_audio_params();
+ if (fd == -1)
+ {
+ g_free(polyp_cfg.playername);
+ polyp_cfg.playername = NULL;
+ g_free(buffer);
+ return 0;
+ }
+ going = 1;
+
+ if (!realtime)
+ pthread_create(&buffer_thread, NULL, polyp_loop, NULL);
+ return 1;
+}
+
+
+static void free_all_objects(void) {
+ if (stream) {
+ pa_stream_unref(stream);
+ stream = NULL;
+ }
+
+ if (context) {
+ pa_context_unref(context);
+ context = NULL;
+ }
+
+ if (glib_mainloop) {
+ pa_glib_mainloop_free(glib_mainloop);
+ glib_mainloop = NULL;
+ }
+
+ mainloop = NULL;
+}
+
+static int polyp_open(AFormat fmt, int rate, int nch) {
+ struct pa_sample_spec ss;
+
+ free_all_objects();
+
+ if (fmt == FMT_U8)
+ ss.format = PA_SAMPLE_U8;
+ else if (fmt == FMT_S16_LE)
+ ss.format = PA_SAMPLE_S16LE;
+ else if (fmt == FM_S16_BE)
+ ss.format = PA_SAMPLE_S16BE;
+ else if (fmt == FM_S16_NE)
+ ss.format = PA_SAMPLE_S16NE;
+ else
+ return 0;
+
+ ss.rate = rate;
+ ss.channels = nch;
+
+ if (!pa_sample_spec_valid(&ss))
+ return 0;
+
+ glib_mainloop = pa_glib_mainloop_new();
+ g_assert(glib_mainloop);
+ mainloop = pa_glib_mainloop_get_api(glib_mainloop);
+ g_assert(mainloop);
+ context = pa_context_new(mainloop, "xmms");
+ assert(context);
+ pa_context_set_state_callback(context, context_state_callback, NULL);
+ pa_context_connect(context, NULL);
+
+ if (wait_for_context_ready() < 0)
+ goto fail;
+
+ stream = pa_stream_new(context, polyp_plugin.filename);
+ assert(stream);
+
+ pa_stream_set_write_callback(stream, stream_state_callback, NULL);
+ pa_stream_connect_playback(stream, NULL, NULL);
+
+ if (wait_for_stream_ready() < 0)
+ goto fail;
+
+ return 1;
+
+fail:
+ free_all_objects();
+}
+
+static void polyp_close(void) {
+
+ if (stream && pa_stream_get_state(stream) == PA_STREAM_READY) {
+ struct pa_operation *o;
+ o = pa_stream_drain(stream, NULL, NULL);
+ assert(o);
+ wait_for_operation(o);
+ }
+
+ if (context && pa_context_get_state(context) == PA_CONTEXT_READY) {
+ struct pa_operation *o;
+ if ((o = pa_context_drain(context, NULL, NULL)))
+ wait_for_operation(o);
+ }
+
+ free_all_objects();
+}
+
+static void polyp_init(void) {
+}
+
+static OutputPlugin polyp_plugin = {
+ NULL,
+ NULL,
+ "Polypaudio Output Plugin", /* Description */
+ polyp_init,
+ NULL, /* polyp_about, */
+ NULL, /* polyp_configure, */
+ polyp_get_volume,
+ polyp_set_volume,
+ polyp_open,
+ polyp_write,
+ polyp_close,
+ polyp_flush,
+ polyp_pause,
+ polyp_free,
+ polyp_playing,
+ polyp_get_output_time,
+ polyp_get_written_time,
+};
+
+OutputPlugin *get_oplugin_info(void) {
+ return &polyp_plugin;
+}