[Sumover-tech] rat with OSS on FreeBSD

Petr Holub hopet at ics.muni.cz
Thu Jul 6 10:00:50 BST 2006


Dear Sumover,

I'd like to raise an issue on FreeBSD - though newpcm works nice
for some cards, many more are supported in 4Front OpenSound (OSS).
Would it be possible to implement a optional switch for OSS on
FreeBSD instead of newpcm? And another issue is whether the auddev_oss
could be updated to support state-of-art OSS API?

Some very preliminary patch for OSS with LynxONE card we use for our
AccessGrid install is below my signature. However, there are some
outstanding issues that need to be sorted out. Probably the most
significant is how to pair half-duplex recording/playout devices
properly (the separate devices are the preferred way for OSS according
to 4Front). With the version of OSS I have and with the LynxONE
card, the devices are in reveresed order compared to what rat
expects. However, with newer version of OSS and different card,
I've found that the devices are ordered differently (all the playout
devices first and then all recording devices IIRC). Thus assuming
any fixed order is probably wrong and some better way is needed.

Best regards,
Petr

================================================================
                            Petr Holub
CESNET z.s.p.o.                       Supercomputing Center Brno
Zikova 4                             Institute of Compt. Science
162 00 Praha 6, CZ                            Masaryk University
Czech Republic                     Botanicka 68a, 60200 Brno, CZ
e-mail: Petr.Holub at cesnet.cz               phone: +420-549493944
                                             fax: +420-541212747
                                       e-mail: hopet at ics.muni.cz


Index: rat/auddev_oss.c
===================================================================
RCS file: /cvs/collab/rat-LYNXONE/rat/auddev_oss.c,v
retrieving revision 1.1
retrieving revision 1.10
diff -u -r1.1 -r1.10
--- rat/auddev_oss.c	13 Jun 2005 10:48:04 -0000	1.1
+++ rat/auddev_oss.c	14 Jun 2005 15:34:43 -0000	1.10
@@ -9,7 +9,7 @@
  */

 #ifndef HIDE_SOURCE_STRINGS
-static const char cvsid[] = "$Id: auddev_oss.c,v 1.1 2005/06/13 10:48:04 hopet
Exp $";
+static const char cvsid[] = "$Id: auddev_oss.c,v 1.10 2005/06/14 15:34:43 hopet
Exp $";
 #endif

 #include "config_unix.h"
@@ -29,6 +29,11 @@
 #endif
 #endif

+#if 1
+#define FOURFRONT_OSS
+#define LYNXONE
+#endif
+
 #ifdef HAVE_ALSA_AUDIO
 #undef UNUSED
 #include <sys/asoundlib.h>
@@ -218,8 +223,13 @@

         ioctl(fd, SNDCTL_DSP_RESET, 0);

+#ifdef FOURFRONT_OSS
+	st = stereo + 1;
+	if (ioctl(fd, SNDCTL_DSP_CHANNELS, &st) == -1 || st != (stereo + 1)) {
+#else
         st = stereo;
         if (ioctl(fd, SNDCTL_DSP_STEREO, &st) == -1 || st != stereo) {
+#endif /* FOURFRONT_OSS */
 		debug_msg("  disabled (%d channels not supported)\n", stereo + 1);
 		return FALSE;
         }
@@ -265,7 +275,11 @@

 	/* Check if the device is full duplex. This MUST be the first test   */
 	/* after the audio device is opened.                                 */
+#ifdef FOURFRONT_OSS
+	if (ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0) == -1 || 1) {
+#else
 	if (ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0) == -1) {
+#endif /* FOURFRONT_OSS */
 		debug_msg("testing %s support for full duplex operation: no\n",
device->audio_rdev);
 		device->duplex = OSS_DUPLEX_HALF;
 	} else {
@@ -279,10 +293,14 @@
                 for (stereo = 0; stereo < 2; stereo++) {
 			debug_msg("testing %s support for %5dHz %s\n", device->audio_rdev,
speed[speed_index], stereo?"stereo":"mono");
                         if (oss_test_mode(fd, speed[speed_index], stereo)) {
+				debug_msg(" ... succeeded.\n");
 				device->supported_formats[device->num_supported_formats].sample_rate =
speed[speed_index];
 				device->supported_formats[device->num_supported_formats].channels    =
stereo + 1;
 				device->num_supported_formats++;
 			}
+			else {
+				debug_msg(" ... failed.\n");
+			}
                 }
         }
 	/* Check for OSS version number */
@@ -330,14 +348,19 @@

 	/* Attempt to open rdev read-only and wdev write-only, both half- */
 	/* duplex, simultaneously. Return TRUE if we succeed.             */
+	debug_msg("Trying to pair read device %s with write device %s\n",
devices[rdev].audio_rdev, devices[wdev].audio_wdev);
+#ifdef FOURFRONT_OSS
+	devices[rdev].audio_rfd = open(devices[rdev].audio_rdev, O_RDONLY);
+#else
 	devices[rdev].audio_rfd = open(devices[rdev].audio_rdev, O_RDONLY | O_NDELAY);
+#endif /* FOURFRONT_OSS */
 	if (devices[rdev].audio_rfd == 0) {
 		debug_msg("unable to open %s %s\n", devices[rdev].audio_rdev,
strerror(errno));
 		return FALSE;
 	}

 	n = read(devices[rdev].audio_rfd, buf, sizeof(buf));
-	debug_msg("read in test_device_pair returns %d\n", n);
+	debug_msg("read in test_device_pair (rdev=%s) returns %d\n",
devices[rdev].audio_rdev, n);
 	if (n < 0) {
 		debug_msg("cannot read audio from %s %s\n", devices[rdev].audio_rdev,
strerror(errno));
 		return FALSE;
@@ -369,7 +392,40 @@
 		return;
 	}

+#ifdef FOURFRONT_OSS
+	for (i = 1; i < num_devices; i++) {
+		debug_msg("Trying to combine rdev %s and wdev %s\n", devices[i].audio_rdev,
devices[i-1].audio_wdev);
+		if ((devices[i].duplex == OSS_DUPLEX_HALF) && (devices[i-1].duplex ==
OSS_DUPLEX_HALF)) {
+			if (oss_test_device_pair(i, i-1)) {
+				debug_msg("Combining %s and %s\n", devices[i].audio_rdev,
devices[i-1].audio_wdev);
+				memcpy(devices[i-1].audio_rdev, devices[i].audio_rdev, 16);
+				if (strlen(devices[i-1].mixer_wdev) != 0) {
+					debug_msg("First mixer is valid\n");
+				} else {
+					debug_msg("First mixer is invalid - assuming second is okay\n");
+					memcpy(devices[i-1].mixer_wdev, devices[i].mixer_wdev, 16);
+				}
+				devices[i-1].audio_rfd = -1;
+				devices[i-1].audio_wfd = -1;
+				devices[i-1].mixer_rfd = -1;
+				devices[i-1].mixer_wfd = -1;
+				devices[i-1].duplex    = OSS_DUPLEX_PAIR;
+				devices[i-1].rec_mask  = devices[i].rec_mask;
+				xfree(devices[i].name);
+				/* Move the rest of the device table down... */
+				for (j = i; j < (num_devices - 1); j++) {
+					devices[j] = devices[j+1];
+				}
+				xmemchk();
+				num_devices--;
+			} else {
+				debug_msg("Cannot pair %s and %s\n", devices[i].audio_rdev,
devices[i+1].audio_wdev);
+			}
+		}
+	}
+#else
 	for (i = 0; i < num_devices - 1; i++) {
+		debug_msg("Trying to combine rdev %s and wdev %s\n", devices[i].audio_rdev,
devices[i+1].audio_wdev);
 		if ((devices[i].duplex == OSS_DUPLEX_HALF) && (devices[i+1].duplex ==
OSS_DUPLEX_HALF)) {
 			if (oss_test_device_pair(i, i+1)) {
 				debug_msg("Combining %s and %s\n", devices[i].audio_rdev,
devices[i+1].audio_wdev);
@@ -399,6 +455,7 @@
 			}
 		}
 	}
+#endif /* FOURFRONT_OSS */
 }

 static void
@@ -500,9 +557,15 @@
 		}
 	}

+#ifdef FOURFRONT_OSS
+	stereo = ifmt->channels;
+	assert(stereo == 1 || stereo == 2);
+	if ((ioctl(fd, SNDCTL_DSP_CHANNELS, &stereo) == -1) || (stereo !=
ifmt->channels )) {
+#else
 	stereo = ifmt->channels - 1;
 	assert(stereo == 0 || stereo == 1);
 	if ((ioctl(fd, SNDCTL_DSP_STEREO, &stereo) == -1) || (stereo !=
(ifmt->channels - 1))) {
+#endif /* FOURFRONT_OSS */
 		debug_msg("device doesn't support %d channels!\n", ifmt->channels);
 		return FALSE;
 	}
@@ -546,14 +609,22 @@
 			/* Open the device, and switch to full duplex mode. */
 			assert(strcmp(devices[ad].audio_rdev, devices[ad].audio_wdev) == 0);

+#ifdef FOURFRONT_OSS
+			devices[ad].audio_rfd = open(devices[ad].audio_rdev, O_RDWR | O_NDELAY |
O_NONBLOCK);
+#else
 			devices[ad].audio_rfd = open(devices[ad].audio_rdev, O_RDWR | O_NDELAY);
+#endif /* FOURFRONT_OSS */
 			if (devices[ad].audio_rfd == 0) {
 				debug_msg("unable to open %s\n", devices[ad].audio_rdev);
 				return FALSE;
 			}
 			devices[ad].audio_wfd = devices[ad].audio_rfd;

+#ifdef FOURFRONT_OSS
+			if (ioctl(devices[ad].audio_rfd, SNDCTL_DSP_SETDUPLEX, 0) == -1 && 0) {
+#else
 			if (ioctl(devices[ad].audio_rfd, SNDCTL_DSP_SETDUPLEX, 0) == -1) {
+#endif /* FOURFRONT_OSS */
 				debug_msg("device doesn't support full duplex operation\n");
 				oss_audio_close(ad);
 				return FALSE;
@@ -570,13 +641,21 @@

 			break;
 		case OSS_DUPLEX_PAIR:
+#ifdef FOURFRONT_OSS
+			devices[ad].audio_rfd = open(devices[ad].audio_rdev, O_RDONLY | O_NDELAY |
O_NONBLOCK);
+#else
 			devices[ad].audio_rfd = open(devices[ad].audio_rdev, O_RDONLY | O_NDELAY);
+#endif /* FOURFRONT_OSS */
 			if (devices[ad].audio_rfd == 0) {
 				debug_msg("unable to open %s\n", devices[ad].audio_rdev);
 				return FALSE;
 			}

+#ifdef FOURFRONT_OSS
+			devices[ad].audio_wfd = open(devices[ad].audio_wdev, O_WRONLY | O_NDELAY |
O_NONBLOCK);
+#else
 			devices[ad].audio_wfd = open(devices[ad].audio_wdev, O_WRONLY | O_NDELAY);
+#endif /* FOURFRONT_OSS */
 			if (devices[ad].audio_wfd == 0) {
 				debug_msg("unable to open %s\n", devices[ad].audio_wdev);
 				return FALSE;
@@ -682,6 +761,12 @@
         assert(ad < OSS_MAX_DEVICES);
         assert(devices[ad].audio_rfd > 0);

+#ifdef LYNXONE
+	assert(iport == AUDIO_LINE_IN);
+	debug_msg("Gain can't be controlled on LYNXONE!\n");
+	return;
+#endif /* LYNXONE */
+
 	switch (iport) {
 		case AUDIO_MICROPHONE :
 			if (devices[ad].is_ac97) {
@@ -865,9 +950,12 @@
         ioctl(devices[ad].audio_rfd, SNDCTL_DSP_GETISPACE, &info);
         available = min(info.bytes, read_bytes);

-        read_len  = read(devices[ad].audio_rfd, (char *)buf, available);
+	read_len  = read(devices[ad].audio_rfd, (char *)buf, available);
+
 	if (read_len < 0) {
-                perror("audio_read");
+		if(errno != EAGAIN) {
+			debug_msg("Failed reading from device %s: %s\n", devices[ad].audio_rdev,
strerror(errno));
+		}
 		return 0;
         }

@@ -885,12 +973,17 @@
         p   = (char *) buf;
         len = write_bytes;
 	errno = 0;
+	if (write_bytes == 0) {
+		return write_bytes;
+	}
         while (1) {
                 if ((done = write(devices[ad].audio_wfd, p, len)) == len) {
                         break;
                 }
-		if (errno != EINTR && errno != EAGAIN) {
-			perror("audio_write");
+		if ((errno != EINTR && errno != EAGAIN) || (done < 0)) {
+			if(errno != EAGAIN) {
+				debug_msg("Failed to write to audio device %s: %s\n",
devices[ad].audio_wdev, strerror(errno));
+			}
 			if(done < 0) done=0;
 			return write_bytes - (len - done);
                 }
@@ -904,17 +997,33 @@
 void
 oss_audio_non_block(audio_desc_t ad)
 {
+#ifndef FOURFRONT_OSS
 	int  on = 1;

         assert(devices[ad].audio_rfd > 0);
         assert(devices[ad].audio_wfd > 0);

 	if (ioctl(devices[ad].audio_rfd, FIONBIO, (char *)&on) < 0) {
-		debug_msg("Failed to set non-blocking mode on audio device!\n");
+		debug_msg("Failed to set non-blocking mode on read audio device!\n");
 	}
 	if (ioctl(devices[ad].audio_wfd, FIONBIO, (char *)&on) < 0) {
-		debug_msg("Failed to set non-blocking mode on audio device!\n");
+		debug_msg("Failed to set non-blocking mode on write audio device!\n");
+	}
+#else
+	UNUSED(ad);
+#if 0
+	/* The following FOURFRONT_OSS API seems to be broken and proper way is
+	   to use O_NONBLOCK flag when opening the device */
+	if (fcntl(devices[ad].audio_rfd, F_SETFL, O_NONBLOCK) < 0) {
+		debug_msg("Failed to set non-blocking mode on read audio device!\n");
+		perror("fcntl rfd");
+	}
+	if (fcntl(devices[ad].audio_wfd, F_SETFL, O_NONBLOCK) < 0) {
+		debug_msg("Failed to set non-blocking mode on write audio device!\n");
+		perror("fcntl wfd");
 	}
+#endif /* on/off 1/0 switch */
+#endif /* FOURFRONT_OSS */
 }

 /* Set ops on audio device to block */
@@ -926,6 +1035,11 @@
         assert(devices[ad].audio_rfd > 0);
         assert(devices[ad].audio_wfd > 0);

+#ifdef FOURFRONT_OSS
+	debug_msg("4Front OSS doesn't support switch to nonblock on the fly.\n");
+	abort();
+#endif /* FOURFRONT_OSS */
+
 	if (ioctl(devices[ad].audio_rfd, FIONBIO, (char *)&on) < 0) {
 		debug_msg("Failed to set blocking mode on audio device!\n");
 	}
@@ -977,6 +1091,11 @@

         UNUSED(ad); assert(devices[ad].mixer_rfd > 0);

+#ifdef LYNXONE
+	debug_msg("Line input selection - the only one LYNXONE has!\n");
+	port = AUDIO_LINE_IN;
+#endif /* LYNXONE */
+
         switch (port) {
 		case AUDIO_MICROPHONE:
 			debug_msg("Trying to select microphone input...\n");
Index: rat/auddev_oss.h
===================================================================
RCS file: /cvs/collab/rat-LYNXONE/rat/auddev_oss.h,v
retrieving revision 1.1
retrieving revision 1.1.1.1
diff -u -r1.1 -r1.1.1.1
--- rat/auddev_oss.h	13 Jun 2005 10:48:04 -0000	1.1
+++ rat/auddev_oss.h	13 Jun 2005 10:48:04 -0000	1.1.1.1
@@ -6,7 +6,7 @@
  * Copyright (c) 1998-2001 University College London
  * All rights reserved.
  *
- * $Id: auddev_oss.h,v 1.1 2005/06/13 10:48:04 hopet Exp $
+ * $Id: auddev_oss.h,v 1.1.1.1 2005/06/13 10:48:04 hopet Exp $
  */

 #ifndef _AUDDEV_OSS_H_
Index: rat/Makefile.in
===================================================================
RCS file: /cvs/collab/rat-LYNXONE/rat/Makefile.in,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- rat/Makefile.in	13 Jun 2005 10:48:04 -0000	1.1
+++ rat/Makefile.in	14 Jun 2005 16:45:14 -0000	1.2
@@ -1,7 +1,7 @@
 #
 # Makefile for the RAT project. This requires GNU make on many systems.
 #
-# $Id: Makefile.in,v 1.1 2005/06/13 10:48:04 hopet Exp $
+# $Id: Makefile.in,v 1.2 2005/06/14 16:45:14 hopet Exp $
 #
 # Configure substitutes variables here... #####################################

@@ -145,7 +145,19 @@
 	@${ECHO} "Generating rat shell script"
 	@${ECHO} "#/bin/sh" > $@
 	@${ECHO} "# Generated automatically from RAT Makefile.  Edit there." >> $@
-	@${ECHO} 'PATH=$${PATH}:.' >> $@
+	@${ECHO} 'PATH=$${PATH}:.:/usr/lib/oss/bin' >> $@
+	@${ECHO} '# echo -n "Reloading OSS to make sure its set up correctly... "' >>
$@
+	@${ECHO} '# soundoff' >> $@
+	@${ECHO} '# sleep 1' >> $@
+	@${ECHO} '# soundon' >> $@
+	@${ECHO} '# sleep 1' >> $@
+	@${ECHO} '# echo "done."' >> $@
+	@${ECHO} 'echo -n "Setting up Lynx card for MoAG setup..."' >> $@
+	@${ECHO} 'ossmix lynxone.ratelock off' >> $@
+	@${ECHO} 'ossmix lynxone.trim -10DB' >> $@
+	@${ECHO} 'sleep 1' >> $@
+	@${ECHO} 'echo "done."' >> $@
+	@${ECHO} 'echo "Executing rat binary."' >> $@
 	@${ECHO} "exec rat-$(VERSION)" '$$*' >> $@
 	@chmod a+x rat

Index: rat/configure.in
===================================================================
RCS file: /cvs/collab/rat-LYNXONE/rat/configure.in,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- rat/configure.in	13 Jun 2005 10:48:04 -0000	1.1
+++ rat/configure.in	13 Jun 2005 11:04:32 -0000	1.2
@@ -1,5 +1,5 @@
 dnl UCL RAT configure script.
-dnl $Header: /cvs/collab/rat-LYNXONE/rat/configure.in,v 1.1 2005/06/13 10:48:04
hopet Exp $
+dnl $Header: /cvs/collab/rat-LYNXONE/rat/configure.in,v 1.2 2005/06/13 11:04:32
hopet Exp $
 dnl
 dnl Process this file with GNU autoconf to generate a configure script.

@@ -209,9 +209,14 @@
 	# Note luigi and newpcm have compatible soundcard.h files but
 	# mixer behaves differently under both systems.  During runtime
 	# only one of these modules will be used.
-	AU_OBJ="$AU_OBJ auddev_luigi.o auddev_newpcm.o"
-	AC_DEFINE(HAVE_LUIGI_AUDIO)
-  	AC_DEFINE(HAVE_NEWPCM_AUDIO)
+	# AU_OBJ="$AU_OBJ auddev_luigi.o auddev_newpcm.o"
+	# AC_DEFINE(HAVE_LUIGI_AUDIO)
+  	# AC_DEFINE(HAVE_NEWPCM_AUDIO)
+	#
+        # New 4Front OSS for FreeBSD
+        AU_OBJ="$AU_OBJ auddev_oss.o"
+        AC_CHECK_HEADERS(sys/soundcard.h soundcard.h)
+        AC_DEFINE(HAVE_OSS_AUDIO)
 	;;
 *netbsd*)
 	AU_OBJ="$AUDIOBJ auddev_netbsd.o"




More information about the Sumover-tech mailing list