Mercurial > audlegacy-plugins
diff src/Output/arts/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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Output/arts/audio.c Mon Sep 18 01:11:49 2006 -0700 @@ -0,0 +1,430 @@ +/* + * aRts ouput plugin for xmms + * + * Copyright (C) 2000,2003 Haavard Kvaalen <havardk@xmms.org> + * + * Licenced under GNU GPL version 2. + * + * Audacious port by Giacomo Lozito from develia.org + * + */ + +#include "arts.h" +#include "arts_helper/arts_helper.h" +#include <errno.h> +#include <signal.h> +#include <pthread.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <unistd.h> + +static gboolean going, paused, helper_failed; +static guint64 written; +static struct params_info input_params, output_params; +static int helperfd; +static pid_t helper_pid; + +static int (*arts_convert_func)(void **data, int length); +struct arts_config artsxmms_cfg; + +struct { + int left, right; +} volume = {100, 100}; + + +typedef struct format_info { + AFormat format; + long frequency; + int channels; + long bps; +} format_info_t; + +static format_info_t input; +/* static format_info_t effect; */ +/* static format_info_t output; */ + + +void artsxmms_tell_audio(AFormat * fmt, gint * srate, gint * nch) +{ + (*fmt) = input.format; + (*srate) = input.frequency; + (*nch) = input.channels; +} + + +void artsxmms_init(void) +{ + ConfigDb *db; + + memset(&artsxmms_cfg, 0, sizeof (artsxmms_cfg)); + + artsxmms_cfg.buffer_size = 400; + + db = bmp_cfg_db_open(); + bmp_cfg_db_get_int(db, "arts", "buffer_size", + &artsxmms_cfg.buffer_size); + bmp_cfg_db_close(db); +} + + +static int read_all(int fd, void *buf, size_t count) +{ + size_t left = count; + int r; + do { + r = read(fd, buf, left); + if (r < 0) + return -1; + left -= r; + buf = (char *)buf + r; + } while (left > 0 && r > 0); + return count - left; +} + +static int write_all(int fd, const void *buf, size_t count) +{ + size_t left = count; + int w; + do { + w = write(fd, buf, left); + if (w < 0) + return -1; + left -= w; + buf = (char *)buf + w; + } while (left > 0 && w > 0); +/* g_message("wrote: %d", count - left); */ + return count - left; +} + +static int wait_for_helper(int fd) +{ + struct timeval timeout; + fd_set rdfs; + int sr; + + FD_ZERO(&rdfs); + FD_SET(fd, &rdfs); + + timeout.tv_sec = 10; + timeout.tv_usec = 0; + + sr = select(fd + 1, &rdfs, NULL, NULL, &timeout); + if (sr < 0) { + g_message("wait_for_helper(): select failed: %s", + strerror(errno)); + return -1; + } else if (!sr) { + g_message("wait_for_helper(): Timed out waiting for helper"); + return -1; + } + return 0; +} + +static int xx; + +static int helper_cmd_data(int cmd, int idata, void* ptr, int data_length) +{ + static pthread_mutex_t artsm = PTHREAD_MUTEX_INITIALIZER; + struct command out; + struct response in; + int status; + + out.cmd = cmd; + out.data = idata; + out.data_length = data_length; + xx++; + + if (helper_failed) + goto failed; + + pthread_mutex_lock(&artsm); +/* fprintf(stderr, "Sending %d; ", out.cmd); */ + if (write_all(helperfd, &out, sizeof (out)) != sizeof (out)) + goto failed; + if (data_length > 0) + if (write_all(helperfd, ptr, data_length) != data_length) + goto failed; + + if (wait_for_helper(helperfd)) { + g_message("waiting failed: %d", cmd); + goto failed; + } + + if (read_all(helperfd, &in, sizeof (in)) != sizeof (in)) + { + g_message("read failed: %d", cmd); + goto failed; + } + +/* fprintf(stderr, "%d complete\n", out.cmd); */ + pthread_mutex_unlock(&artsm); + + if (in.status) + return -in.status; + return in.data; + + failed: + g_message("helper_cmd_data(): failed"); + helper_failed = TRUE; + if (helper_pid && waitpid(helper_pid, &status, WNOHANG)) { + if (status) + g_message("helper terminated abnormally: %d", status); + else + g_message("helper terminated normally"); + helper_pid = 0; + } else if (helper_pid) + g_message("helper has not terminated"); + pthread_mutex_unlock(&artsm); + return -STATUS_FAILED; +} + +static int helper_cmd(int cmd, int idata) +{ + return helper_cmd_data(cmd, idata, NULL, 0); +} + +static int artsxmms_helper_init(struct params_info *params) +{ + int ret; + struct init_data id; + + id.version = HELPER_VERSION; + id.resolution = params->resolution; + id.rate= params->frequency; + id.nchannels = params->channels; + id.buffer_time = artsxmms_cfg.buffer_size; + + ret = helper_cmd_data(CMD_INIT, 0, &id, sizeof (id)); + if (ret) { + g_message("Init failed: %d", -ret); + return -1; + } + + return 0; +} + +static void artsxmms_set_params(struct params_info *params, AFormat fmt, int rate, int nch) +{ + params->format = fmt; + params->frequency = rate; + params->channels = nch; + + params->bps = rate * nch; + params->resolution = 8; + if (!(fmt == FMT_U8 || fmt == FMT_S8)) + { + params->bps *= 2; + params->resolution = 16; + } +} + +int artsxmms_get_written_time(void) +{ + if (!going) + return 0; + + return (written * 1000) / output_params.bps; +} + +int artsxmms_get_output_time(void) +{ + int time; + + if (!going) + return 0; + if (helper_failed) + return -2; + + time = artsxmms_get_written_time(); + time -= helper_cmd(CMD_GET_OUTPUT_LATENCY, 0); + + if (time < 0) + return 0; + return time; +} + +int artsxmms_playing(void) +{ + if (!going) + return FALSE; + + if (!paused) + { + if (helper_cmd(CMD_QUERY_PLAYING, 0) <= 0) + return FALSE; + return TRUE; + } + + return TRUE; +} + +int artsxmms_free(void) +{ + int space; + + if (!going) + return 0; + + space = helper_cmd(CMD_FREE, 0); + if (space < 0) + return 0; + + return space; +} + +void artsxmms_write(gpointer ptr, int length) +{ + AFormat new_format; + int new_frequency, new_channels; + EffectPlugin *ep; + + new_format = input_params.format; + new_frequency = input_params.frequency; + new_channels = input_params.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 != output_params.format || + new_frequency != output_params.frequency || + new_channels != output_params.channels) + { + /* + * The effect plugins has changed the format of the stream. + */ + + guint64 offset = (written * 1000) / output_params.bps; + artsxmms_set_params(&output_params, new_format, + new_frequency, new_channels); + arts_convert_func = arts_get_convert_func(output_params.format); + + written = (offset * output_params.bps) / 1000; + + artsxmms_helper_init(&output_params); + } + + /* + * Doing the effect plugin processing here adds some latency, + * but the alternative is just too frigging hairy. + */ + + if (effects_enabled() && ep && ep->mod_samples) + length = ep->mod_samples(&ptr, length, input_params.format, + input_params.frequency, + input_params.channels); + + if (arts_convert_func) + arts_convert_func(ptr, length); + + helper_cmd_data(CMD_WRITE, 0, ptr, length); + written += length; +} + +void artsxmms_close(void) +{ + int status; + going = 0; +/* g_message("sending quit cmd"); */ + if (!helper_cmd(CMD_QUIT, 0)) { + waitpid(helper_pid, &status, 0); + if (status) + g_message("artsxmms_close(): Child exited abnormally: %d", + status); + } +} + +void artsxmms_flush(int time) +{ + /* + * Argh, no way to flush the stream from the C api. + */ + written = (time / 10) * (output_params.bps / 100); + +} + +void artsxmms_pause(short p) +{ + paused = p; + helper_cmd(CMD_PAUSE, p); +} + +static int artsxmms_start_helper() +{ + int sockets[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) + { + g_message("artsxmms_start_helper(): " + "Failed to create socketpair: %s", strerror(errno)); + return -1; + } + + if ((helper_pid = fork()) == 0) + { + /* Child */ + char sockfdstr[10]; + close(sockets[1]); + sprintf(sockfdstr, "%d", sockets[0]); + execlp("audacious-arts-helper", "audacious-arts-helper", + sockfdstr, NULL); + g_warning("artsxmms_start_helper(): " + "Failed to start audacious-arts-helper: %s", strerror(errno)); + close(sockets[0]); + _exit(1); + } + close(sockets[0]); + helperfd = sockets[1]; + + if (helper_pid < 0) + { + g_message("artsxmms_start_helper(): " + "Failed to fork() helper process: %s", strerror(errno)); + close(sockets[1]); + return -1; + } + + return 0; +} + +int artsxmms_open(AFormat fmt, int rate, int nch) +{ + if (artsxmms_start_helper() < 0) + return 0; + + artsxmms_set_params(&input_params, fmt, rate, nch); + artsxmms_set_params(&output_params, fmt, rate, nch); + + arts_convert_func = arts_get_convert_func(output_params.format); + + written = 0; + paused = 0; + helper_failed = FALSE; + + if (artsxmms_helper_init(&output_params)) { + artsxmms_close(); + return 0; + } + artsxmms_set_volume(volume.left, volume.right); + + going = 1; + return 1; +} + +void artsxmms_get_volume(int *l, int *r) +{ + *l = volume.left; + *r = volume.right; +} + +void artsxmms_set_volume(int l, int r) +{ + int vol[2]; + volume.left = l; + volume.right = r; + vol[0] = l; + vol[1] = r; + helper_cmd_data(CMD_SET_VOLUME, 0, vol, sizeof(vol)); +}