changeset 3276:e279cc05f189

-ao NAS support by Tobias Diedrich <ranma@gmx.at>
author arpi
date Mon, 03 Dec 2001 01:13:48 +0000
parents 38344371432f
children 9d4a7b4afba2
files DOCS/German/sound.html DOCS/sound.html Makefile configure libao2/ao_nas.c libao2/audio_out.c
diffstat 6 files changed, 366 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/DOCS/German/sound.html	Mon Dec 03 01:09:36 2001 +0000
+++ b/DOCS/German/sound.html	Mon Dec 03 01:13:48 2001 +0000
@@ -14,6 +14,7 @@
 
 <TR><TD>&nbsp;&nbsp;</TD><TD VALIGN=top><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>oss</TD><TD>&nbsp;&nbsp;</TD><TD><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>OSS (ioctl) Treiber</TD></TR>
 <TR><TD></TD><TD VALIGN=top><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>sdl</TD><TD></TD><TD><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>SDL Treiber (unterstützt Up-/Downsampling, <B>ESD</B>, <B>ARTS</B> usw.)</TD></TR>
+<TR><TD></TD><TD VALIGN=top><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>nas</TD><TD></TD><TD><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>NAS (Network Audio System) Treiber</TD></TR>
 <TR><TD></TD><TD VALIGN=top><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>alsa5</TD><TD></TD><TD><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>ALSA 0.5 Treiber</TD></TR>
 <TR><TD></TD><TD VALIGN=top><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>alsa9</TD><TD></TD><TD><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>ALSA 0.9 Treiber (funkioniert, macht aber Probleme -> verwende OSS)</TD></TR>
 <TR><TD></TD><TD VALIGN=top><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>sun</TD><TD></TD><TD><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>SUN Audio-Treiber (/dev/audio) für BSD und Solaris8 Anwender</TD></TR>
--- a/DOCS/sound.html	Mon Dec 03 01:09:36 2001 +0000
+++ b/DOCS/sound.html	Mon Dec 03 01:13:48 2001 +0000
@@ -14,6 +14,7 @@
 
 <TR><TD>&nbsp;&nbsp;</TD><TD VALIGN=top><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>oss</TD><TD>&nbsp;&nbsp;</TD><TD><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>OSS (ioctl) driver</TD></TR>
 <TR><TD></TD><TD VALIGN=top><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>sdl</TD><TD></TD><TD><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>SDL driver (supports up/downsampling, <B>ESD</B>, <B>ARTS</B> etc)</TD></TR>
+<TR><TD></TD><TD VALIGN=top><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>nas</TD><TD></TD><TD><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>NAS (Network Audio System) driver</TD></TR>
 <TR><TD></TD><TD VALIGN=top><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>alsa5</TD><TD></TD><TD><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>native ALSA 0.5 driver</TD></TR>
 <TR><TD></TD><TD VALIGN=top><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>alsa9</TD><TD></TD><TD><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>native ALSA 0.9 driver (works, but has problems -> use OSS)</TD></TR>
 <TR><TD></TD><TD VALIGN=top><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>sun</TD><TD></TD><TD><FONT face="Verdana, Arial, Helvetica, sans-serif" size=2>SUN audio driver (/dev/audio) for BSD and Solaris8 users</TD></TR>
--- a/Makefile	Mon Dec 03 01:09:36 2001 +0000
+++ b/Makefile	Mon Dec 03 01:13:48 2001 +0000
@@ -34,7 +34,7 @@
 VO_LIBS = -Llibvo2 -lvo2 $(X_LIB) $(DXR3_LIB) $(GGI_LIB) $(MLIB_LIB) $(PNG_LIB) $(SDL_LIB) $(SVGA_LIB)
 endif
 
-A_LIBS = -Lmp3lib -lMP3 -Llibac3 -lac3 $(ALSA_LIB) $(MAD_LIB) $(VORBIS_LIB) $(SGIAUDIO_LIB)
+A_LIBS = -Lmp3lib -lMP3 -Llibac3 -lac3 $(ALSA_LIB) $(NAS_LIB) $(MAD_LIB) $(VORBIS_LIB) $(SGIAUDIO_LIB)
 
 OSDEP_LIBS = -Llinux -losdep
 PP_LIBS = -Lpostproc -lpostproc
--- a/configure	Mon Dec 03 01:09:36 2001 +0000
+++ b/configure	Mon Dec 03 01:13:48 2001 +0000
@@ -646,6 +646,7 @@
 _dga=auto	# 1 2 no auto
 _xv=auto
 _sdl=auto
+_nas=auto
 _png=auto
 _gl=auto
 _ggi=auto
@@ -714,6 +715,8 @@
   --disable-xv)		_xv=no		;;
   --enable-sdl)		_sdl=yes	;;
   --disable-sdl)	_sdl=no		;;
+  --enable-nas)		_nas=yes	;;
+  --disable-nas)	_nas=no		;;
   --enable-png)		_png=yes	;;
   --disable-png)	_png=no		;;
   --enable-gl)		_gl=yes		;;
@@ -1768,6 +1771,24 @@
 fi
 echores "$_sdl (with $_sdlconfig)"
 
+echocheck "NAS"
+if test "$_nas" = auto || test "$_nas" = yes ; then
+  cat > $TMPC << EOF
+#include <audio/audiolib.h>
+int main(void) { return 0; }
+EOF
+  _nas=no
+  cc_check -laudio -lX11 -lXt -L$_x11libdir && _nas=yes
+fi
+if test "$_nas" = yes ; then
+  _def_nas='#define HAVE_NAS 1'
+  _ld_nas="-laudio -lX11 -lXt -L$_x11libdir"
+  _aosrc="$_aosrc ao_nas.c"
+  _aomodules="nas $_aomodules"
+else
+  _def_nas='#undef HAVE_NAS'
+fi
+echores "$_nas"
 
 echocheck "DXR3/H+"
 if test "$_dxr3" = auto ; then
@@ -2598,6 +2619,7 @@
 
 # audio output
 ALSA_LIB = $_ld_alsa
+NAS_LIB = $_ld_nas
 MAD_LIB = $_ld_mad
 VORBIS_LIB = $_ld_vorbis
 SGIAUDIO_LIB = $_ld_sgiaudio
@@ -2779,6 +2801,8 @@
 $_def_alsa9
 $_def_sunaudio
 $_def_sgiaudio
+$_def_nas
+
 
 /* Enable fast OSD/SUB renderer (looks ugly, but uses less CPU power) */
 #undef FAST_OSD
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libao2/ao_nas.c	Mon Dec 03 01:13:48 2001 +0000
@@ -0,0 +1,333 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <audio/audiolib.h>
+
+#include "audio_out.h"
+#include "audio_out_internal.h"
+#include "afmt.h"
+
+#define FRAG_SIZE 4096
+#define FRAG_COUNT 8
+#define BUFFER_SIZE FRAG_SIZE * FRAG_COUNT
+
+#define NAS_DEBUG 0
+
+#if NAS_DEBUG == 1
+#define DPRINTF(format, args...)	fprintf(stderr, format, ## args); \
+					fflush(stderr)
+#else
+#define DPRINTF(format, args...)
+#endif
+
+static ao_info_t info = 
+{
+	"NAS audio output",
+	"nas",
+	"Tobias Diedrich",
+	""
+};
+
+static	AuServer*	aud;
+static	AuFlowID	flow;
+static	AuDeviceID	dev;
+
+static void *client_buffer;
+static int client_buffer_size = BUFFER_SIZE;
+static int client_buffer_used;
+static int server_buffer_size = BUFFER_SIZE;
+static int server_buffer_used;
+static pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+pthread_t event_thread;
+static int stop_thread;
+
+LIBAO_EXTERN(nas)
+
+static void wait_for_event()
+{
+	AuEvent ev;
+
+	AuNextEvent(aud, AuTrue, &ev);
+	AuDispatchEvent(aud, &ev);
+}
+
+static int readBuffer(int num)
+{
+	pthread_mutex_lock(&buffer_mutex);
+	DPRINTF("readBuffer(): num=%d client=%d/%d server=%d/%d\n",
+			num,
+			client_buffer_used, client_buffer_size,
+			server_buffer_used, server_buffer_size);
+
+	if (client_buffer_used == 0) {
+		DPRINTF("buffer is empty, nothing read.\n");
+		pthread_mutex_unlock(&buffer_mutex);
+		return 0;
+	}
+	if (client_buffer_used < num)
+		num = client_buffer_used;
+
+	AuWriteElement(aud, flow, 0, num, client_buffer, AuFalse, NULL);
+	client_buffer_used -= num;
+	server_buffer_used += num;
+	memmove(client_buffer, client_buffer + num, client_buffer_used);
+	pthread_mutex_unlock(&buffer_mutex);
+
+	return num;
+}
+
+static void writeBuffer(void *data, int len)
+{
+	pthread_mutex_lock(&buffer_mutex);
+	DPRINTF("writeBuffer(): len=%d client=%d/%d server=%d/%d\n",
+			len, client_buffer_used, client_buffer_size,
+			server_buffer_used, server_buffer_size);
+
+	memcpy(client_buffer + client_buffer_used, data, len);
+	client_buffer_used += len;
+
+	pthread_mutex_unlock(&buffer_mutex);
+	if (server_buffer_used < server_buffer_size)
+		readBuffer(server_buffer_size - server_buffer_used);
+}
+
+
+static void *event_thread_start(void *data)
+{
+	while (!stop_thread) {
+		wait_for_event();
+	}
+}
+
+static AuBool event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd)
+{
+	switch (ev->type) {
+	case AuEventTypeElementNotify: {
+		AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
+		DPRINTF("event_handler(): kind %d state %d->%d reason %d numbytes %d\n",
+				event->kind,
+				event->prev_state,
+				event->cur_state,
+				event->reason,
+				event->num_bytes);
+
+		switch (event->kind) {
+		case AuElementNotifyKindLowWater:
+			server_buffer_used -= event->num_bytes;
+			readBuffer(event->num_bytes);
+			break;
+		case AuElementNotifyKindState:
+			if ((event->cur_state == AuStatePause) &&
+				(event->reason != AuReasonUser)) {
+				// buffer underrun -> refill buffer
+				server_buffer_used = 0;
+				readBuffer(server_buffer_size - server_buffer_used);
+			}
+		}
+		}
+	}
+	return AuTrue;
+}
+
+static AuBool error_handler(AuServer* aud, AuErrorEvent* ev)
+{
+	char s[100];
+	AuGetErrorText(aud, ev->error_code, s, 100);
+	fprintf(stderr,"libaudiooss: error [%s]\n"
+		"error_code: %d\n"
+		"request_code: %d\n"
+		"minor_code: %d\n",
+		s,
+		ev->error_code,
+		ev->request_code,
+		ev->minor_code);
+	fflush(stderr);
+
+	return AuTrue;
+}
+
+static AuDeviceID find_device(int nch)
+{
+	int i;
+	for (i = 0; i < AuServerNumDevices(aud); i++) {
+		AuDeviceAttributes *dev = AuServerDevice(aud, i);
+		if ((AuDeviceKind(dev) == AuComponentKindPhysicalOutput) &&
+		     AuDeviceNumTracks(dev) == nch) {
+			return AuDeviceIdentifier(dev);
+		}
+	}
+	return AuNone;
+}
+
+static unsigned char aformat_to_auformat(unsigned int format)
+{
+	switch (format) {
+	case	AFMT_U8:	return AuFormatLinearUnsigned8;
+	case	AFMT_S8:	return AuFormatLinearSigned8;
+	case	AFMT_U16_LE:	return AuFormatLinearUnsigned16LSB;
+	case	AFMT_U16_BE:	return AuFormatLinearUnsigned16MSB;
+	case	AFMT_S16_LE:	return AuFormatLinearSigned16LSB;
+	case	AFMT_S16_BE:	return AuFormatLinearSigned16MSB;
+	case	AFMT_MU_LAW:	return AuFormatULAW8;
+	default: return 0;
+	}
+}
+
+// to set/get/query special features/parameters
+static int control(int cmd,int arg){
+	return -1;
+}
+
+// open & setup audio device
+// return: 1=success 0=fail
+static int init(int rate,int channels,int format,int flags)
+{
+	AuElement elms[3];
+	AuStatus as;
+	unsigned char auformat = aformat_to_auformat(format);
+	int bytes_per_sample;
+	char *server;
+
+	printf("ao2: %d Hz  %d chans  %s\n",rate,channels,
+	audio_out_format_name(format));
+
+	if (!auformat) {
+		printf("Unsupported format -> nosound\n");
+		return 0;
+	}
+
+	client_buffer = malloc(BUFFER_SIZE);
+	bytes_per_sample = channels * AuSizeofFormat(auformat);
+
+	ao_data.samplerate = rate;
+	ao_data.channels = channels;
+	ao_data.buffersize = BUFFER_SIZE * 2;
+	ao_data.outburst = FRAG_SIZE;
+	ao_data.bps = rate * bytes_per_sample;
+
+	if (!bytes_per_sample) {
+		printf("Zero bytes per sample -> nosound\n");
+		return 0;
+	}
+
+	if (!(server = getenv("AUDIOSERVER")))
+		server = getenv("DISPLAY");
+
+	if (!server) // default to tcp/localhost:8000
+		server = "tcp/localhost:8000";
+
+	printf("Using audioserver %s\n", server);
+
+	aud = AuOpenServer(server, 0, NULL, 0, NULL, NULL);
+	if (!aud){ 
+		printf("Can't open nas audio server -> nosound\n");
+		return 0;
+	}
+
+	dev = find_device(channels);
+	if ((dev == AuNone) || (!(flow = AuCreateFlow(aud, NULL)))) {
+		printf("Can't find a device serving that many channels -> nosound\n");
+		AuCloseServer(aud);
+		aud = 0;
+		return 0;
+	}
+
+	AuMakeElementImportClient(elms, rate, auformat, channels, AuTrue,
+				BUFFER_SIZE / bytes_per_sample,
+				(BUFFER_SIZE - FRAG_SIZE) / bytes_per_sample,
+				0, NULL);
+	AuMakeElementExportDevice(elms+1, 0, dev, rate,
+				AuUnlimitedSamples, 0, NULL);
+	AuSetElements(aud, flow, AuTrue, 2, elms, &as);
+	if (as != AuSuccess)
+		printf("AuSetElements returned status %d!\n", as);
+	AuRegisterEventHandler(aud, AuEventHandlerIDMask |
+				AuEventHandlerTypeMask,
+				AuEventTypeElementNotify, flow,
+				event_handler, (AuPointer) NULL);
+	AuSetErrorHandler(aud, error_handler);
+	AuStartFlow(aud, flow, &as);
+	if (as != AuSuccess)
+		printf("AuSetElements returned status %d!\n", as);
+
+	/*
+	 * Wait for first buffer underrun event
+	 *
+	 * For some weird reason we get a buffer underrun event if we
+	 * don't fill the server buffer fast enough after staring the
+	 * flow. So we just wait for it to happen to be in a sane state.
+	 */
+	wait_for_event();
+
+	pthread_create(&event_thread, NULL, &event_thread_start, NULL);
+
+	return 1;
+}
+
+// close audio device
+static void uninit(){
+	stop_thread = 1;
+	pthread_join(event_thread, NULL);
+	AuStopFlow(aud, flow, NULL);
+	AuCloseServer(aud);
+	aud = 0;
+	free(client_buffer);
+}
+
+// stop playing and empty buffers (for seeking/pause)
+static void reset(){
+	pthread_mutex_lock(&buffer_mutex);
+	client_buffer_used = 0;
+	pthread_mutex_unlock(&buffer_mutex);
+	while (server_buffer_used > 0) {
+		usleep(1000);
+//		DPRINTF("used=%d\n", server_buffer_used);
+	}
+}
+
+// stop playing, keep buffers (for pause)
+static void audio_pause()
+{
+    // for now, just call reset();
+    reset();
+}
+
+// resume playing, after audio_pause()
+static void audio_resume()
+{
+}
+
+// return: how many bytes can be played without blocking
+static int get_space()
+{
+	int result;
+
+	pthread_mutex_lock(&buffer_mutex);
+	result = client_buffer_size - client_buffer_used;
+	pthread_mutex_unlock(&buffer_mutex);
+
+	return result;
+}
+
+// plays 'len' bytes of 'data'
+// it should round it down to outburst*n
+// return: number of bytes played
+static int play(void* data,int len,int flags){
+	writeBuffer(data, len);
+//    printf("ao_nas: wrote %d bytes of audio data\n", len);
+	return len;
+}
+
+// return: delay in seconds between first and last sample in buffer
+static float get_delay()
+{
+	float result;
+
+	pthread_mutex_lock(&buffer_mutex);
+	result = ((float)(client_buffer_used + server_buffer_used)) /
+		(float)ao_data.bps;
+	pthread_mutex_unlock(&buffer_mutex);
+
+	return result;
+}
--- a/libao2/audio_out.c	Mon Dec 03 01:09:36 2001 +0000
+++ b/libao2/audio_out.c	Mon Dec 03 01:13:48 2001 +0000
@@ -22,6 +22,9 @@
 #ifdef HAVE_ESD
  extern ao_functions_t audio_out_esd;
 #endif
+#ifdef HAVE_NAS
+extern ao_functions_t audio_out_nas;
+#endif
 #ifdef HAVE_SDL
 extern ao_functions_t audio_out_sdl;
 #endif
@@ -60,6 +63,9 @@
 #ifdef HAVE_ESD
 	&audio_out_esd,
 #endif
+#ifdef HAVE_NAS
+	&audio_out_nas,
+#endif
 #ifdef HAVE_SDL
         &audio_out_sdl,
 #endif