From aece210569f06a9b0936bdfc839951176107a558 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 23 Aug 2004 18:51:41 +0000 Subject: initial commit git-svn-id: file:///home/lennart/svn/public/xmms-pulse/trunk@3 ef929aba-56e2-0310-84e0-b7573d389508 --- src/audio.c | 486 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 486 insertions(+) create mode 100644 src/audio.c 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; +} -- cgit