From f855ed495e161b83f939552d00ba9bd2a95978ff Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 26 Aug 2004 15:32:51 +0000 Subject: rename some stuff git-svn-id: file:///home/lennart/svn/public/xmms-pulse/trunk@6 ef929aba-56e2-0310-84e0-b7573d389508 --- src/audio.c | 486 ----------------------------------------------------------- src/plugin.c | 461 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/thread.c | 461 -------------------------------------------------------- 3 files changed, 461 insertions(+), 947 deletions(-) delete mode 100644 src/audio.c create mode 100644 src/plugin.c delete mode 100644 src/thread.c diff --git a/src/audio.c b/src/audio.c deleted file mode 100644 index b5cf8b8..0000000 --- a/src/audio.c +++ /dev/null @@ -1,486 +0,0 @@ -#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; -} diff --git a/src/plugin.c b/src/plugin.c new file mode 100644 index 0000000..4f9b515 --- /dev/null +++ b/src/plugin.c @@ -0,0 +1,461 @@ +#include + +#include + +static pthread_cond_t request_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t request_mutex = PTHREAD_MUTEX_INITIALIZER; + +struct request { + enum { + MESSAGE_OPEN, + MESSAGE_CLOSE, + MESSAGE_WRITE, + MESSAGE_FLUSH, + MESSAGE_PAUSE, + MESSAGE_UNPAUSE, + MESSAGE_LATENCY, + MESSAGE_WRITABLE, + MESSAGE_TRIGGER, + } type; + void *data; + struct pa_sample_spec ss; + size_t length; + int success, done; + uint32_t value; + pa_volume_t volume; +}; + +static struct request* current_request = NULL; + +static int pipe_fds[2] = { -1, -1}; + +static pthread_t thread_id; +static int thread_running = 0; +static struct pa_context *context = NULL; +static struct pa_stream *stream = NULL; +static struct pa_mainloop_api *mainloop_api = NULL; +static int failed = 0; +static pa_volume_t volume = PA_VOLUME_NORM; +static size_t written = 0; +static pa_usec_t latency; +static int do_trigger; + +static void finish_request(int success) { + failed = 1; + + pthread_mutex_lock(&request_mutex); + + if (current_request) { + current_request->done = 1; + current_request->success = success; + pthread_cond_signal(&request_cond); + } + + pthread_mutex_unlock(&request_mutex); +} + +static void info_callback(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) { + assert(c && c == context); + + if (!i) + return; + + volume = i->volume; +} + +static void subscribe_callback(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) { + assert(c && c == context); + + if (!stream || index != pa_stream_get_index(stream) || t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE)) + return; + + pa_operation_unref(pa_context_get_sink_input_info(c, index, info_callback, NULL); +} + +static void stream_state_callback(struct pa_stream *s, void *userdata) { + assert(stream == s); + + switch(pa_stream_get_state(c)) { + case PA_STREAM_CREATING: + break; + case PA_STREAM_READY: + assert(current_request && current_request->type == MESSAGE_OPEN); + pa_operation_unref(pa_context_get_sink_input_info(context, pa_stream_get_index(s), info_callback, NULL); + finish_request(1); + break; + default: + finish_request(0); + } +} + +static void context_state_callback(struct pa_context *c, void *userdata) { + assert(c && c == context); + + switch (pa_context_get_state(c)) { + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + + case PA_CONTEXT_READY : + assert(!stream && current_request); + pa_context_set_subscribe_callback(context, subscribe_callback, NULL); + pa_operation_unref(pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL); + + stream = pa_stream_new(c, ¤t_request->ss); + assert(stream); + + pa_stream_set_state(stream, stream_state_callback); + pa_stream_connect_playback(stream, NULL, NULL); + break; + + default: + finish_request(0); + } +} + +static void context_drain_callback(struct pa_context *c, void *userdata) { + assert(c && c == context); + mainloop_api->quit(mainloop_api, 0); +} + +static void stream_success_callback(struct pa_stream *s, int success, void *userdata) { + assert(s == stream && s); + finish_request(!!success); +} + +static void context_success_callback(struct pa_context *c, int success, void *userdata) { + assert(c == context && c); + finish_request(!!success); +} + +static void latency_callback(struct pa_stream *s, pa_usec_t latency, void *userdata) { + assert(s == stream && s); + assert(current_request && current_request>type == MESSAGE_LATENCY); + current_request->value = latency; + finish_request(latency != (pa_usec_t) -1); +} + +static void request_func(struct pa_mainloop*api, struct pa_io_event *io, enum pa_io_event_flags f, void *userdata) { + char x; + + assert(api && io && f == PA_IO_EVENT_INPUT); + + read(pipe_fds[0], &x, 1); + + pthread_mutex_lock(&request_mutex); + + if (current_request) { + if (failed) { + fail(); + } else { + switch (current_request->type) { + case MESSAGE_OPEN: + assert(!context && !stream); + context = pa_context_new(api, "xmms"); + assert(context); + pa_context_set_state_callback(context, context_state_callback, NULL); + pa_context_connect(context, NULL); + break; + + case MESSAGE_CLOSE: { + struct pa_operation *o; + assert(context); + if ((o = pa_context_drain(context, context_drain_callback, NULL))) + pa_operation_unref(o); + else + api->quit(mainloop_api, 0); + break; + } + + case MESSAGE_WRITE: + assert(context && stream && current_request->data && current_request->length > 0); + pa_stream_write(stream, current_request->data, current_request->length, free, 0); + current_request->data = NULL; + finish_request(1); + break; + + case MESSAGE_FLUSH: + assert(context && stream); + pa_operation_unref(pa_stream_flush(stream, stream_success_callback, NULL)); + break; + + case MESSAGE_PAUSE: + case MESSAGE_UNPAUSE: + assert(context && stream); + pa_operation_unref(pa_stream_cork(stream, current_request->type == MESSAGE_UNPAUSE, stream_success_callback, NULL)); + break; + + case MESSAGE_LATENCY: + assert(context && stream); + pa_operation_unref(pa_stream_get_latency(stream, latency_callback, NULL)); + break; + + case MESSAGE_WRITABLE: + assert(context && stream); + current_request->value = pa_stream_writable_size(stream); + finish_request(1); + break; + + case MESSAGE_GETVOLUME: + assert(context && stream); + current_request->volume = volume; + finish_request(1); + break; + + case MESSAGE_SETVOLUME: + assert(context && stream); + pa_operation_unref(pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), current_request->volume, context_success_callback, NULL)); + break; + + case MESSAGE_TRIGGER: + assert(context && stream); + pa_operation_unref(pa_stream_trigger(stream, stream_success_callback, NULL)); + break; + + } + } + } + + pthread_mutex_unlock(&request_mutex); +} + +static void* thread_func(void *t) { + struct pa_mainloop *m; + struct pa_io_event *io; + + assert(pipe_fds[0] >= 0 && !mainloop_api); + + failed = 0; + + m = pa_mainloop_new(); + assert(m); + mainloop_api = pa_mainloop_get_api(m); + assert(mainloop_api); + + io = mainloop_api->io_new(mainloop_api, pipe_fds[0], PA_IO_EVENT_INPUT, &request_func, NULL); + assert(io); + + pa_mainloop_run(m, NULL); + + api->io_free(io); + pa_mainloop_free(m); + + mainloop_api = NULL; + + return NULL; +} + +static void start_thread(void) { + int r; + assert(!thread_running); + + r = pipe(pipe_fds); + assert(r >= 0 && pipe_fds[0] >= 0 && pipe_fds[1] >= 0); + + current_request = NULL; + + r = pthread_create(&thread_id, NULL, thread_func, NULL); + assert(!r); +} + +static void stop_thread(void) { + struct request req; + assert(thread_running); + + pthread_join(thread_id, NULL); + + thread_running = 0; + assert(!current_request); + + close(pipe_fds[0]); + close(pipe_fds[1]); + pipe_fds[0] = pipe_fds[1] = -1; +} + +static void polyp_get_volume(int *l, int *r) { + struct request r; + int v; + + r.message = MESSAGE_GET_VOLUME; + request_execute(&r); + + v = (r.volume*100)/PA_VOLUME_NORM; + + *r = *l = v > 100 ? 100 : v; +} + +void polyp_set_volume(int l, int r) { + struct request r; + + r.message = MESSAGE_SET_VOLUME; + r.volume = ((l+r)*PA_VOLUME_NORM)/200; + request_execute(&r); +} + +static void request_execute(struct request *r) { + char x = 'x'; + assert(r); + + r->success = r->done = 0; + + pthread_mutex_lock(&request_mutex); + assert(!current_request); + current_request = r; + + assert(pipe_fds[1] >= 0); + write(pipe_fds[1], &x, sizeof(x)); + + while (!r->done) + pthread_cond_wait(&request_cond, &request_mutex); + + current_request = NULL; + + pthread_mutex_unlock(&request_mutex); +} + +static void polyp_pause(short b) { + struct request r; + + r.message = b ? MESSAGE_PAUSE : MESSAGE_UNPAUSE; + request_execute(&r); +} + +static int polyp_free(void) { + int ret; + struct request r; + r.message = MESSAGE_WRITABLE; + request_execute(&r); + + ret = r.value; + + if (do_trigger) { + r.message = MESSAGE_TRIGGER; + request_execute(&r); + } + + do_trigger = 1; + return ret; +} + +static int polyp_playing(void) { + struct request r; + r.message = MESSAGE_LATENCY; + request_execute(&r); + + return r.value != 0; +} + +static int polyp_get_written_time(void) { + return ((written/pa_frame_size(&sample_spec))*1000)/sample_spec.rate; +} + +static int polyp_get_output_time(void) { + int t, ms; + struct request r; + r.message = MESSAGE_LATENCY; + request_execute(&r); + + t = polyp_get_output_time(); + ms = r.value/1000; + + if (ms > t) + return 0; + else + return t-ms; +} + +static void polyp_flush(int time) { + struct request r; + r.message = MESSAGE_FLUSH; + request_execute(&r); + + written = (time*sample_spec.rate/1000)*pa_frame_size(&sample_spec); +} + +static void polyp_write(void* ptr, int length) { + struct request r; + r.message = MESSAGE_WRITE; + r.data = ptr; + r.length = length; + + request_execute(&r); + + written += length; + do_trigger = 0; +} + +static int polyp_open(AFormat fmt, int rate, int nch) { + struct request r; + + if (fmt == FMT_U8) + r.ss.format = PA_SAMPLE_U8; + else if (fmt == FMT_S16_LE) + r.ss.format = PA_SAMPLE_S16LE; + else if (fmt == FM_S16_BE) + r.ss.format = PA_SAMPLE_S16BE; + else if (fmt == FM_S16_NE) + r.ss.format = PA_SAMPLE_S16NE; + else + return 0; + + r.ss.rate = rate; + r.ss.channels = nch; + + if (!pa_sample_spec_valid(&r.ss)) + return 0; + + start_thread(); + + r.message = MESSAGE_OPEN; + request_execute(&r); + + if (!r->success) { + stop_thread(); + return 0; + } + + written = do_trigger = 0; + + return 1; +} + +static void polyp_close(void) { + struct request r; + + assert(thread_running); + + r.message = MESSAGE_CLOSE; + request_execute(&r); + + stop_thread(); +} + + +static void polyp_init(void) { +} + +static OutputPlugin polyp_plugin = { + NULL, + NULL, + "Polypaudio Output Plugin", /* Description */ + polyp_init, /* done */ + NULL, /* polyp_about, */ + NULL, /* polyp_configure, */ + polyp_get_volume, + polyp_set_volume, + + polyp_open, /* done */ + polyp_write, /* done */ + polyp_close, /* done */ + polyp_flush, /* done */ + polyp_pause, /* done */ + polyp_free, /* done */ + polyp_playing, /* done */ + polyp_get_output_time, /* done */ + polyp_get_written_time, /* done */ +}; + +OutputPlugin *get_oplugin_info(void) { + return &polyp_plugin; +} diff --git a/src/thread.c b/src/thread.c deleted file mode 100644 index 4f9b515..0000000 --- a/src/thread.c +++ /dev/null @@ -1,461 +0,0 @@ -#include - -#include - -static pthread_cond_t request_cond = PTHREAD_COND_INITIALIZER; -static pthread_mutex_t request_mutex = PTHREAD_MUTEX_INITIALIZER; - -struct request { - enum { - MESSAGE_OPEN, - MESSAGE_CLOSE, - MESSAGE_WRITE, - MESSAGE_FLUSH, - MESSAGE_PAUSE, - MESSAGE_UNPAUSE, - MESSAGE_LATENCY, - MESSAGE_WRITABLE, - MESSAGE_TRIGGER, - } type; - void *data; - struct pa_sample_spec ss; - size_t length; - int success, done; - uint32_t value; - pa_volume_t volume; -}; - -static struct request* current_request = NULL; - -static int pipe_fds[2] = { -1, -1}; - -static pthread_t thread_id; -static int thread_running = 0; -static struct pa_context *context = NULL; -static struct pa_stream *stream = NULL; -static struct pa_mainloop_api *mainloop_api = NULL; -static int failed = 0; -static pa_volume_t volume = PA_VOLUME_NORM; -static size_t written = 0; -static pa_usec_t latency; -static int do_trigger; - -static void finish_request(int success) { - failed = 1; - - pthread_mutex_lock(&request_mutex); - - if (current_request) { - current_request->done = 1; - current_request->success = success; - pthread_cond_signal(&request_cond); - } - - pthread_mutex_unlock(&request_mutex); -} - -static void info_callback(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) { - assert(c && c == context); - - if (!i) - return; - - volume = i->volume; -} - -static void subscribe_callback(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) { - assert(c && c == context); - - if (!stream || index != pa_stream_get_index(stream) || t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE)) - return; - - pa_operation_unref(pa_context_get_sink_input_info(c, index, info_callback, NULL); -} - -static void stream_state_callback(struct pa_stream *s, void *userdata) { - assert(stream == s); - - switch(pa_stream_get_state(c)) { - case PA_STREAM_CREATING: - break; - case PA_STREAM_READY: - assert(current_request && current_request->type == MESSAGE_OPEN); - pa_operation_unref(pa_context_get_sink_input_info(context, pa_stream_get_index(s), info_callback, NULL); - finish_request(1); - break; - default: - finish_request(0); - } -} - -static void context_state_callback(struct pa_context *c, void *userdata) { - assert(c && c == context); - - switch (pa_context_get_state(c)) { - case PA_CONTEXT_CONNECTING: - case PA_CONTEXT_AUTHORIZING: - case PA_CONTEXT_SETTING_NAME: - break; - - case PA_CONTEXT_READY : - assert(!stream && current_request); - pa_context_set_subscribe_callback(context, subscribe_callback, NULL); - pa_operation_unref(pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL); - - stream = pa_stream_new(c, ¤t_request->ss); - assert(stream); - - pa_stream_set_state(stream, stream_state_callback); - pa_stream_connect_playback(stream, NULL, NULL); - break; - - default: - finish_request(0); - } -} - -static void context_drain_callback(struct pa_context *c, void *userdata) { - assert(c && c == context); - mainloop_api->quit(mainloop_api, 0); -} - -static void stream_success_callback(struct pa_stream *s, int success, void *userdata) { - assert(s == stream && s); - finish_request(!!success); -} - -static void context_success_callback(struct pa_context *c, int success, void *userdata) { - assert(c == context && c); - finish_request(!!success); -} - -static void latency_callback(struct pa_stream *s, pa_usec_t latency, void *userdata) { - assert(s == stream && s); - assert(current_request && current_request>type == MESSAGE_LATENCY); - current_request->value = latency; - finish_request(latency != (pa_usec_t) -1); -} - -static void request_func(struct pa_mainloop*api, struct pa_io_event *io, enum pa_io_event_flags f, void *userdata) { - char x; - - assert(api && io && f == PA_IO_EVENT_INPUT); - - read(pipe_fds[0], &x, 1); - - pthread_mutex_lock(&request_mutex); - - if (current_request) { - if (failed) { - fail(); - } else { - switch (current_request->type) { - case MESSAGE_OPEN: - assert(!context && !stream); - context = pa_context_new(api, "xmms"); - assert(context); - pa_context_set_state_callback(context, context_state_callback, NULL); - pa_context_connect(context, NULL); - break; - - case MESSAGE_CLOSE: { - struct pa_operation *o; - assert(context); - if ((o = pa_context_drain(context, context_drain_callback, NULL))) - pa_operation_unref(o); - else - api->quit(mainloop_api, 0); - break; - } - - case MESSAGE_WRITE: - assert(context && stream && current_request->data && current_request->length > 0); - pa_stream_write(stream, current_request->data, current_request->length, free, 0); - current_request->data = NULL; - finish_request(1); - break; - - case MESSAGE_FLUSH: - assert(context && stream); - pa_operation_unref(pa_stream_flush(stream, stream_success_callback, NULL)); - break; - - case MESSAGE_PAUSE: - case MESSAGE_UNPAUSE: - assert(context && stream); - pa_operation_unref(pa_stream_cork(stream, current_request->type == MESSAGE_UNPAUSE, stream_success_callback, NULL)); - break; - - case MESSAGE_LATENCY: - assert(context && stream); - pa_operation_unref(pa_stream_get_latency(stream, latency_callback, NULL)); - break; - - case MESSAGE_WRITABLE: - assert(context && stream); - current_request->value = pa_stream_writable_size(stream); - finish_request(1); - break; - - case MESSAGE_GETVOLUME: - assert(context && stream); - current_request->volume = volume; - finish_request(1); - break; - - case MESSAGE_SETVOLUME: - assert(context && stream); - pa_operation_unref(pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), current_request->volume, context_success_callback, NULL)); - break; - - case MESSAGE_TRIGGER: - assert(context && stream); - pa_operation_unref(pa_stream_trigger(stream, stream_success_callback, NULL)); - break; - - } - } - } - - pthread_mutex_unlock(&request_mutex); -} - -static void* thread_func(void *t) { - struct pa_mainloop *m; - struct pa_io_event *io; - - assert(pipe_fds[0] >= 0 && !mainloop_api); - - failed = 0; - - m = pa_mainloop_new(); - assert(m); - mainloop_api = pa_mainloop_get_api(m); - assert(mainloop_api); - - io = mainloop_api->io_new(mainloop_api, pipe_fds[0], PA_IO_EVENT_INPUT, &request_func, NULL); - assert(io); - - pa_mainloop_run(m, NULL); - - api->io_free(io); - pa_mainloop_free(m); - - mainloop_api = NULL; - - return NULL; -} - -static void start_thread(void) { - int r; - assert(!thread_running); - - r = pipe(pipe_fds); - assert(r >= 0 && pipe_fds[0] >= 0 && pipe_fds[1] >= 0); - - current_request = NULL; - - r = pthread_create(&thread_id, NULL, thread_func, NULL); - assert(!r); -} - -static void stop_thread(void) { - struct request req; - assert(thread_running); - - pthread_join(thread_id, NULL); - - thread_running = 0; - assert(!current_request); - - close(pipe_fds[0]); - close(pipe_fds[1]); - pipe_fds[0] = pipe_fds[1] = -1; -} - -static void polyp_get_volume(int *l, int *r) { - struct request r; - int v; - - r.message = MESSAGE_GET_VOLUME; - request_execute(&r); - - v = (r.volume*100)/PA_VOLUME_NORM; - - *r = *l = v > 100 ? 100 : v; -} - -void polyp_set_volume(int l, int r) { - struct request r; - - r.message = MESSAGE_SET_VOLUME; - r.volume = ((l+r)*PA_VOLUME_NORM)/200; - request_execute(&r); -} - -static void request_execute(struct request *r) { - char x = 'x'; - assert(r); - - r->success = r->done = 0; - - pthread_mutex_lock(&request_mutex); - assert(!current_request); - current_request = r; - - assert(pipe_fds[1] >= 0); - write(pipe_fds[1], &x, sizeof(x)); - - while (!r->done) - pthread_cond_wait(&request_cond, &request_mutex); - - current_request = NULL; - - pthread_mutex_unlock(&request_mutex); -} - -static void polyp_pause(short b) { - struct request r; - - r.message = b ? MESSAGE_PAUSE : MESSAGE_UNPAUSE; - request_execute(&r); -} - -static int polyp_free(void) { - int ret; - struct request r; - r.message = MESSAGE_WRITABLE; - request_execute(&r); - - ret = r.value; - - if (do_trigger) { - r.message = MESSAGE_TRIGGER; - request_execute(&r); - } - - do_trigger = 1; - return ret; -} - -static int polyp_playing(void) { - struct request r; - r.message = MESSAGE_LATENCY; - request_execute(&r); - - return r.value != 0; -} - -static int polyp_get_written_time(void) { - return ((written/pa_frame_size(&sample_spec))*1000)/sample_spec.rate; -} - -static int polyp_get_output_time(void) { - int t, ms; - struct request r; - r.message = MESSAGE_LATENCY; - request_execute(&r); - - t = polyp_get_output_time(); - ms = r.value/1000; - - if (ms > t) - return 0; - else - return t-ms; -} - -static void polyp_flush(int time) { - struct request r; - r.message = MESSAGE_FLUSH; - request_execute(&r); - - written = (time*sample_spec.rate/1000)*pa_frame_size(&sample_spec); -} - -static void polyp_write(void* ptr, int length) { - struct request r; - r.message = MESSAGE_WRITE; - r.data = ptr; - r.length = length; - - request_execute(&r); - - written += length; - do_trigger = 0; -} - -static int polyp_open(AFormat fmt, int rate, int nch) { - struct request r; - - if (fmt == FMT_U8) - r.ss.format = PA_SAMPLE_U8; - else if (fmt == FMT_S16_LE) - r.ss.format = PA_SAMPLE_S16LE; - else if (fmt == FM_S16_BE) - r.ss.format = PA_SAMPLE_S16BE; - else if (fmt == FM_S16_NE) - r.ss.format = PA_SAMPLE_S16NE; - else - return 0; - - r.ss.rate = rate; - r.ss.channels = nch; - - if (!pa_sample_spec_valid(&r.ss)) - return 0; - - start_thread(); - - r.message = MESSAGE_OPEN; - request_execute(&r); - - if (!r->success) { - stop_thread(); - return 0; - } - - written = do_trigger = 0; - - return 1; -} - -static void polyp_close(void) { - struct request r; - - assert(thread_running); - - r.message = MESSAGE_CLOSE; - request_execute(&r); - - stop_thread(); -} - - -static void polyp_init(void) { -} - -static OutputPlugin polyp_plugin = { - NULL, - NULL, - "Polypaudio Output Plugin", /* Description */ - polyp_init, /* done */ - NULL, /* polyp_about, */ - NULL, /* polyp_configure, */ - polyp_get_volume, - polyp_set_volume, - - polyp_open, /* done */ - polyp_write, /* done */ - polyp_close, /* done */ - polyp_flush, /* done */ - polyp_pause, /* done */ - polyp_free, /* done */ - polyp_playing, /* done */ - polyp_get_output_time, /* done */ - polyp_get_written_time, /* done */ -}; - -OutputPlugin *get_oplugin_info(void) { - return &polyp_plugin; -} -- cgit