diff src/Output/arts/arts_helper/arts_helper.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/arts_helper/arts_helper.c	Mon Sep 18 01:11:49 2006 -0700
@@ -0,0 +1,446 @@
+/*
+ *  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_helper.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/select.h>
+
+#include <artsc.h>
+
+#define FALSE 0
+#define TRUE (!FALSE)
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+#ifdef WORDS_BIGENDIAN
+#define INT16_FROM_LE(val) ((val & 0xff) << 8 | (val & 0xff00) >> 8)
+#define INT16_TO_LE(val)  ((val & 0xff) << 8 | (val & 0xff00) >> 8)
+#else
+#define INT16_FROM_LE(val) (val)
+#define INT16_TO_LE(val) (val)
+#endif
+
+/* This is not quite portable, but should be ok anyway */
+typedef short int16;
+
+static arts_stream_t handle;
+
+static int going, paused, inited;
+static struct params_info output_params;
+
+struct {
+	int left, right;
+} volume = {100, 100};
+
+struct params_info
+{
+	int resolution;
+	int frequency;
+	int channels;
+
+	/* Cache */
+	int bps;
+};
+
+static struct {
+	unsigned int bsize, latency, psize;
+} arts_params;
+
+static struct {
+	char *ptr;
+	int size;
+	int rd, wr;
+} ring_buffer;
+
+static void artsxmms_set_params(struct params_info *params, int resolution, int rate, int nch)
+{
+	params->frequency = rate;
+	params->channels = nch;
+
+	params->bps = rate * nch;
+	params->resolution = resolution;
+	if (resolution == 16)
+		params->bps *= 2;
+}
+
+static int artsxmms_free(void)
+{
+	if (ring_buffer.rd > ring_buffer.wr)
+		return ring_buffer.rd - ring_buffer.wr - 1;
+	return ring_buffer.size - (ring_buffer.wr - ring_buffer.rd);
+}
+
+static int artsxmms_buffer_used(void)
+{
+	return ring_buffer.size - artsxmms_free();
+}
+
+static int artsxmms_get_output_latency(void) 
+{
+	int latency;
+	long long w;
+	
+	if (!going)
+		return 0;
+
+	w = artsxmms_buffer_used();
+	w += arts_params.bsize - arts_stream_get(handle, ARTS_P_BUFFER_SPACE);
+	latency =  (w * 1000) / output_params.bps;
+
+	if (!paused)
+		latency += arts_params.latency;
+
+	return latency;
+}
+
+static int artsxmms_playing(void)
+{
+	if (!going)
+		return FALSE;
+	
+	if (!paused)
+	{
+		int t;
+		t = arts_stream_get(handle, ARTS_P_BUFFER_SPACE);
+		return t < arts_params.bsize - arts_params.psize;
+	}
+
+	return TRUE;
+}
+
+static void artsxmms_open_stream(struct params_info *params, int buffer_time)
+{
+	int buffersize = buffer_time * params->bps / 1000;
+	handle = arts_play_stream(params->frequency, params->resolution,
+				  params->channels, "XMMS");
+	arts_params.bsize = arts_stream_set(handle, ARTS_P_BUFFER_SIZE,
+					    buffersize);
+	arts_params.latency = arts_stream_get(handle, ARTS_P_SERVER_LATENCY);
+	arts_params.psize = arts_stream_get(handle, ARTS_P_PACKET_SIZE);
+}
+
+static void artsxmms_set_volume(int l, int r)
+{
+	volume.left = l;
+	volume.right = r;
+}
+
+static void volume_adjust(void* data, int length)
+{
+	int i;
+
+	if ((volume.left == 100 && volume.right == 100) ||
+	    (output_params.channels == 1 &&
+	     (volume.left == 100 || volume.right == 100)))
+		return;
+
+	if (output_params.resolution == 16)
+	{
+		int16 *ptr = data;
+		if (output_params.channels == 2)
+			for (i = 0; i < length; i += 4)
+			{
+				*ptr = INT16_TO_LE(INT16_FROM_LE(*ptr) *
+						   volume.left / 100);
+				ptr++;
+				*ptr = INT16_TO_LE(INT16_FROM_LE(*ptr) *
+						   volume.right / 100);
+				ptr++;
+			}
+		else
+		{
+			int vol = MAX(volume.left, volume.right);
+			for (i = 0; i < length; i += 2, ptr++)
+			{
+				*ptr = INT16_TO_LE(INT16_FROM_LE(*ptr) *
+						   vol / 100);
+			}
+		}
+	}
+	else
+	{
+		unsigned char *ptr = data;
+		if (output_params.channels == 2)
+			for (i = 0; i < length; i += 2)
+			{
+				*ptr = *ptr * volume.left / 100;
+				ptr++;
+				*ptr = *ptr * volume.right / 100;
+				ptr++;
+			}
+		else
+		{
+			int vol = MAX(volume.left, volume.right);
+			for (i = 0; i < length; i++, ptr++)
+			{
+				*ptr = *ptr * vol / 100;
+			}
+		}
+	}
+}
+
+static void artsxmms_write(char *ptr, int length)
+{
+	int cnt;
+
+	/* FIXME: Check that length is not too large? */
+	while (length > 0)
+	{
+		cnt = MIN(length, ring_buffer.size - ring_buffer.wr);
+		memcpy(ring_buffer.ptr + ring_buffer.wr, ptr, cnt);
+		ring_buffer.wr = (ring_buffer.wr + cnt) % ring_buffer.size;
+		length -= cnt;
+		ptr += cnt;
+	}
+}
+
+static void artsxmms_write_arts(void)
+{
+	int ret, cnt, space;
+	char *ptr;
+
+	if (ring_buffer.wr == ring_buffer.rd || paused)
+		return;
+	
+	space = arts_stream_get(handle, ARTS_P_BUFFER_SPACE);
+
+	while (space > 0 && ring_buffer.wr != ring_buffer.rd)
+	{
+		if (ring_buffer.wr > ring_buffer.rd)
+			cnt = MIN(space, ring_buffer.wr - ring_buffer.rd);
+		else
+			cnt = MIN(space, ring_buffer.size - ring_buffer.rd);
+
+		ptr = ring_buffer.ptr + ring_buffer.rd;
+
+		volume_adjust(ptr, cnt);
+		ret = arts_write(handle, ptr, cnt);
+		if (ret < 0)
+		{
+			/* FIXME: handle this better? */
+			fprintf(stderr, "artsxmms_write(): write error: %s\n",
+				arts_error_text(ret));
+			return;
+		}
+
+		ring_buffer.rd = (ring_buffer.rd + cnt) % ring_buffer.size;
+		space -= cnt;
+	}
+}
+
+static void artsxmms_close(void)
+{
+	going = 0;
+	arts_close_stream(handle);
+	arts_free();
+}
+
+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);
+	return count - left;
+}
+
+
+static int init_ring_buffer(int size)
+{
+	free(ring_buffer.ptr);
+	/* Make the ring buffer always end on a sample boundary */
+	size -= size % 4;
+	ring_buffer.size = size;
+	ring_buffer.ptr = malloc(size);
+	ring_buffer.rd = 0;
+	ring_buffer.wr = 0;
+	if (ring_buffer.ptr == NULL)
+		return -1;
+	return 0;
+}
+
+static int helper_init(struct init_data *init)
+{
+	int buffer_time = MAX(init->buffer_time, 50);
+	if (init->version != HELPER_VERSION) {
+		fprintf(stderr,
+			"Fatal: Version mismatch between arts output plugin and\n"
+			"       audacious-arts-helper program.\n");
+		return -1;
+	}
+	if (!inited)
+		return -1;
+	artsxmms_set_params(&output_params, init->resolution, init->rate,
+			    init->nchannels);
+
+	if (init_ring_buffer((buffer_time * 2 * output_params.bps) / 1000))
+		return -1;
+
+	if (handle)
+		arts_close_stream(handle);
+	artsxmms_open_stream(&output_params, buffer_time);
+
+	going = 1;
+	return 0;
+}
+
+static int process_cmd(int fd)
+{
+	struct command inp;
+	struct response outp;
+	void *data = NULL;
+	int retval = 0;
+
+	if (read_all(fd, &inp, sizeof(inp)) != sizeof(inp)) {
+		fprintf(stderr, "read short, giving up\n");
+		return -1;
+	}
+	if (inp.data_length > 0) {
+		data = malloc(inp.data_length);
+		if (data == NULL)
+			return -1;
+		if (read_all(fd, data, inp.data_length) != inp.data_length) {
+			fprintf(stderr, "data read short, giving up\n");
+			return -1;
+		}
+	}
+	outp.cmd = inp.cmd;
+	outp.status = STATUS_OK;
+	outp.data = 0;
+/*  	fprintf(stderr, "Recieved %d; ", inp.cmd); */
+	switch (inp.cmd) {
+		case CMD_QUIT:
+			artsxmms_close();
+			retval = 1;
+			break;
+		case CMD_INIT:
+			if (inp.data_length != sizeof (struct init_data))
+				outp.status = STATUS_FAILED;
+			else if (helper_init(data))
+				outp.status = STATUS_FAILED;
+			break;
+		case CMD_PAUSE:
+			paused = inp.data;
+			break;
+		case CMD_SET_VOLUME: {
+			int *vol = data;
+			if (inp.data_length < 2 * sizeof(int)) {
+				outp.status = STATUS_FAILED;
+				break;
+			}
+			artsxmms_set_volume(vol[0], vol[1]);
+			break;
+		}
+		case CMD_WRITE:
+			artsxmms_write(data, inp.data_length);
+			break;
+		case CMD_FREE:
+			outp.data = artsxmms_free();
+			break;
+		case CMD_GET_OUTPUT_LATENCY:
+			outp.data = artsxmms_get_output_latency();
+			break;
+		case CMD_QUERY_PLAYING:
+			outp.data = artsxmms_playing();
+			break;
+		default:
+			outp.status = STATUS_UNKNOWN;
+			fprintf(stderr, "Unknown command %d\n", inp.cmd);
+	}
+	free(data);
+	if (write_all(fd, &outp, sizeof (outp)) != sizeof (outp))
+		return -1;
+	return retval;
+}
+
+
+static int main_loop(int fd)
+{
+	int retval = 0, sr;
+	struct timeval timeout;
+	fd_set rdfs;
+
+	for (;;) {
+		FD_ZERO(&rdfs);
+		FD_SET(fd, &rdfs);
+		timeout.tv_sec = 0;
+		timeout.tv_usec = 20000;
+		sr = select(fd + 1, &rdfs, NULL, NULL, &timeout);
+		if (sr < 0) {
+			fprintf(stderr, "audacious-arts-helper select failed: %s\n",
+				strerror(errno));
+			retval = -1;
+			break;
+		} else if (sr) {
+			int p = process_cmd(fd);
+			if (p < 0) {
+				fprintf(stderr, "cmd failed\n");
+				retval = 1;
+				break;
+			} else if (p)
+				break;
+		}
+
+		artsxmms_write_arts();
+	}
+	return retval;
+}
+
+int main(int argc, char **argv)
+{
+	int fd, err, ret;
+
+	if (argc != 2 || (fd = atoi(argv[1])) < 1)
+	{
+		fprintf(stderr, "Usage: audacious-arts-helper fd\n");
+		return 1;
+	}
+
+	inited = 1;
+
+	if ((err = arts_init()) != 0)
+	{
+		fprintf(stderr, "artsxmms_open(): Unable to initialize aRts: %s\n",
+			  arts_error_text(err));
+		inited = 0;
+	}
+
+	ret = main_loop(fd);
+	close(fd);
+/* 	fprintf(stderr, "helper exits\n"); */
+	return ret < 0;
+}