patches/gnome-media-09-cdda.diff
author bviswa
Fri, 23 Jul 2004 14:02:21 +0000
changeset 3410 5cb45623e963
permissions -rw-r--r--
2004-07-23 Balamurali Viswanathan <[email protected]> Patch from Kaushal Kumar <[email protected]> * gnome-media.spec: Update * patches/gnome-media-09-cdda.diff: Patch to enable gnome-cd play through the sound card on machines where there is no audio cable Fixes bug #5061178

--- gnome-media-2.6.1.org/configure.in	2004-07-20 11:37:36.014702000 +0100
+++ gnome-media-2.6.1/configure.in	2004-07-20 18:35:47.540697000 +0100
@@ -220,6 +220,7 @@ AC_MSG_RESULT($close_tray)
 
 host=`uname -s`
 
+gnomecd_type_sun=false
 CDROM_HOST_FREEBSD=false
 CDROM_HOST_LINUX=false
 CDROM_HOST_SOLARIS=false
@@ -238,6 +239,7 @@ case "$host" in
 	CDROM_HOST=solaris
 	CDROM_HOST_SOLARIS=true
 	default_cd_device="/vol/dev/aliases/cdrom0"
+	gnomecd_type_sun=true
 	;;
 esac
 
@@ -251,6 +253,7 @@ AM_CONDITIONAL(CDROM_HOST_SOLARIS, test 
 
 AC_SUBST(default_cd_device)
 AC_DEFINE_UNQUOTED(default_cd_device, "$default_cd_device")
+AM_CONDITIONAL(CDDA_SOLARIS, $gnomecd_type_sun)
 
 PKG_CHECK_MODULES(GAILUTIL, gail >= 0.0.3)
 
--- gnome-media-2.6.1.org/gnome-cd/Makefile.am	2004-07-20 11:37:30.802152000 +0100
+++ gnome-media-2.6.1/gnome-cd/Makefile.am	2004-07-20 18:35:27.326998000 +0100
@@ -21,6 +21,15 @@ EXTRA_DIST = solaris-cdrom.c		\
 	     linux-cdrom.h		\
 	     gnome-cd.png gnome-cd.desktop.in gnome-cd.schemas.in
 
+if CDDA_SOLARIS
+GNOMECD_CDDA_SRCS = cdda-solaris.h     \
+		    reader-solaris.h   \
+		    writer-solaris.h   \
+		    reader-solaris.c   \
+		    writer-solaris.c
+endif
+
+
 ### intermediate libtool library generated from IDL source
 IDLS = $(top_srcdir)/cddb-slave2/GNOME_Media_CDDBSlave2.idl
 
@@ -64,6 +73,7 @@ gnome_cd_SOURCES =		\
 	cdrom.h			\
 	display.c		\
 	display.h		\
+	$(GNOMECD_CDDA_SRCS)    \
 	$(cd_sources)		\
 	preferences.c		\
 	preferences.h		\
--- gnome-media-2.6.1.org/gnome-cd/cdrom.h	2004-07-20 11:37:30.866377000 +0100
+++ gnome-media-2.6.1/gnome-cd/cdrom.h	2004-07-20 18:35:27.380857000 +0100
@@ -110,6 +110,8 @@ struct _GnomeCDRom {
 
 	GnomeCDRomDeviceLifetime lifetime;
 	GnomeCDRomPrivate *priv;
+
+	void *cdda;
 };
 
 struct _GnomeCDRomClass {
--- gnome-media-2.6.1.org/gnome-cd/callbacks.c	2004-07-20 11:37:30.848050000 +0100
+++ gnome-media-2.6.1/gnome-cd/callbacks.c	2004-07-20 18:35:27.365939000 +0100
@@ -718,6 +718,10 @@ cd_status_changed_cb (GnomeCDRom *cdrom,
                        gtk_widget_set_sensitive (gcd->back_b, FALSE);
 		else
                        gtk_widget_set_sensitive (gcd->back_b, FALSE);
+		if (cdrom->cdda != NULL) {
+			gtk_widget_set_sensitive (gcd->ffwd_b, FALSE);
+			gtk_widget_set_sensitive (gcd->rewind_b, FALSE);
+		}
 		break;
 		
 	case GNOME_CDROM_STATUS_NO_DISC:
--- gnome-media-2.6.1.org/gnome-cd/solaris-cdrom.c	2004-07-20 11:37:30.909612000 +0100
+++ gnome-media-2.6.1/gnome-cd/solaris-cdrom.c	2004-07-20 18:35:27.404927000 +0100
@@ -19,14 +19,19 @@
 #include <libdevinfo.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <gtk/gtk.h>
 
 #include "gnome-cd.h"
 #include "solaris-cdrom.h"
+#include "cdda-solaris.h"
+#include "writer-solaris.h"
+
 
 extern gboolean debug_mode;
 
 static GnomeCDRomClass *parent_class = NULL;
 gboolean cdrom_drive_present = FALSE;
+gboolean cdda_present = TRUE;
 
 typedef struct _SolarisCDRomTrackInfo {
 	char *name;
@@ -62,6 +67,8 @@ static char                     prev_nam
 static int              findcdroms(di_node_t node, di_minor_t minor, void *arg);static void             findevs();
 static int              find_devpath(di_devlink_t devlink, void *arg);
 static int              is_cdrom(di_node_t node, di_minor_t minor);
+int msf2lba(SolarisCDRomTrackInfo *);
+void lba2msf(SolarisCDRom *, int, struct cdrom_subchnl *);
 
 
 static gboolean solaris_cdrom_eject (GnomeCDRom *cdrom,
@@ -387,6 +394,10 @@ solaris_cdrom_eject (GnomeCDRom *cdrom,
 		return FALSE;
 	}
 
+	if (cdrom->cdda) {
+		audio_stop (cdrom->cdda);
+	}
+
 	if (status->cd != GNOME_CDROM_STATUS_TRAY_OPEN) {
 		if (ioctl (cdrom->fd, CDROMEJECT, 0) < 0) {
 			if (error) {
@@ -541,6 +552,8 @@ solaris_cdrom_play (GnomeCDRom *cdrom,
 	GnomeCDRomStatus *status;
 	struct cdrom_msf msf;
 	int minutes, seconds, frames;
+	int start_lba, end_lba;
+	int ret;
 
 	lcd = SOLARIS_CDROM (cdrom);
 	priv = lcd->priv;
@@ -548,6 +561,12 @@ solaris_cdrom_play (GnomeCDRom *cdrom,
 		return FALSE;
 	}
 
+	if (cdda_present && cdrom->cdda == NULL) {
+		cdda_check(&cdrom->cdda, cdrom->fd);
+	}
+	if (cdrom->cdda == NULL)
+		cdda_present = FALSE;
+
 	if (gnome_cdrom_get_status (cdrom, &status, error) == FALSE) {
 		solaris_cdrom_close (lcd);
 		g_free (status);
@@ -608,6 +627,36 @@ solaris_cdrom_play (GnomeCDRom *cdrom,
 	case GNOME_CDROM_AUDIO_STOP:
 	case GNOME_CDROM_AUDIO_ERROR:
 	default:
+
+		if (cdrom->cdda) {
+			start_lba = msf2lba (&priv->track_info[start_track-1]);
+			end_lba = msf2lba (&priv->track_info [priv->number_tracks]);
+			audio_stop (cdrom->cdda);
+			ret = audio_start (cdrom->cdda, start_lba, end_lba);
+			if (ret == -1) {
+				GtkWidget *dialog;
+				dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
+								 GTK_MESSAGE_ERROR,
+								 GTK_BUTTONS_CLOSE,
+								 "Audio Device is busy, or being used by other application",
+								 NULL);
+				g_signal_connect_swapped (GTK_OBJECT (dialog), "response",
+							  G_CALLBACK (gtk_widget_destroy),
+							  GTK_OBJECT (dialog));
+				gtk_widget_show_all (dialog);
+				gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+				if (error) {
+					*error = g_error_new (GNOME_CDROM_ERROR,
+							      GNOME_CDROM_ERROR_SYSTEM_ERROR,
+							      "(solaris_cdrom_play): Audio Device is busy, or being used by other application");
+				}
+				solaris_cdrom_close (lcd);
+				g_free (status);
+				return FALSE;
+			}
+			g_free (status);
+			return TRUE;
+		}
 		/* Start playing */
 		if (start == NULL) {
 			msf.cdmsf_min0 = status->absolute.minute;
@@ -698,6 +747,13 @@ solaris_cdrom_pause (GnomeCDRom *cdrom,
 	}
 
 	if (status->audio == GNOME_CDROM_AUDIO_PAUSE) {
+		if (cdrom->cdda) {
+			audio_resume (cdrom->cdda);
+			g_free (status);
+			solaris_cdrom_close (lcd);
+			return TRUE;
+		}
+
 		if (ioctl (cdrom->fd, CDROMRESUME) < 0) {
 			if (error) {
 				*error = g_error_new (GNOME_CDROM_ERROR,
@@ -717,6 +773,13 @@ solaris_cdrom_pause (GnomeCDRom *cdrom,
 	}
 
 	if (status->audio == GNOME_CDROM_AUDIO_PLAY) {
+		if (cdrom->cdda ) {
+			audio_pause (cdrom->cdda);
+			g_free (status);
+			solaris_cdrom_close (lcd);
+			return TRUE;
+		}
+
 		if (ioctl (cdrom->fd, CDROMPAUSE, 0) < 0) {
 			if (error) {
 				*error = g_error_new (GNOME_CDROM_ERROR,
@@ -764,6 +827,13 @@ solaris_cdrom_stop (GnomeCDRom *cdrom,
 	}
 #endif
 
+	if (cdrom->cdda) {
+		audio_stop (cdrom->cdda);
+		solaris_cdrom_close (lcd);
+		g_free (status);
+		return TRUE;
+	}
+
 	if (ioctl (cdrom->fd, CDROMSTOP, 0) < 0) {
 		if (error) {
 			*error = g_error_new (GNOME_CDROM_ERROR,
@@ -914,6 +984,7 @@ solaris_cdrom_get_status (GnomeCDRom *cd
 	static gboolean port_set = FALSE;
 	int vol_fd;
 	int cd_status;
+	int lba;
 	int i, j;
 
 	g_return_val_if_fail (status != NULL, TRUE);
@@ -927,7 +998,6 @@ solaris_cdrom_get_status (GnomeCDRom *cd
 
 	if (solaris_cdrom_open (lcd, error) == FALSE) {
 		static gboolean function_called = FALSE;
-		solaris_cdrom_close (lcd);
 		if (!function_called)
 		find_cdrom (); 
 		function_called = TRUE;
@@ -1020,19 +1090,27 @@ solaris_cdrom_get_status (GnomeCDRom *cd
 
 #endif
 
-	subchnl.cdsc_format = CDROM_MSF;
-	if (ioctl (cdrom->fd, CDROMSUBCHNL, &subchnl) < 0) {
-		if (error) {
-			*error = g_error_new (GNOME_CDROM_ERROR,
-					      GNOME_CDROM_ERROR_SYSTEM_ERROR,
-					      "(solaris_cdrom_get_status): CDROMSUBCHNL ioctl failed %s",
-					      strerror (errno));
+	if (cdrom->cdda) {
+		memset (&subchnl, 0, sizeof (subchnl));
+		lba = audio_get_state (cdrom->cdda, &subchnl);
+		if (lba) {
+			lba2msf (lcd, lba, &subchnl);
 		}
+	} else {
+		subchnl.cdsc_format = CDROM_MSF;
+		if (ioctl (cdrom->fd, CDROMSUBCHNL, &subchnl) < 0) {
+			if (error) {
+				*error = g_error_new (GNOME_CDROM_ERROR,
+					      	      GNOME_CDROM_ERROR_SYSTEM_ERROR,
+					      	      "(solaris_cdrom_get_status): CDROMSUBCHNL ioctl failed %s",
+					              strerror (errno));
+			}
 
-		solaris_cdrom_close (lcd);
-		g_free (realstatus);
-		*status = NULL;
-		return FALSE;
+			solaris_cdrom_close (lcd);
+			g_free (realstatus);
+			*status = NULL;
+			return FALSE;
+		}
 	}
 	/* get initial volume */
 	vol_fd = open ( "/dev/audioctl", O_RDONLY);
@@ -1364,3 +1442,64 @@ gnome_cdrom_new (const char      *cdrom_
 		g_object_new (solaris_cdrom_get_type (), NULL),
 		cdrom_device, update, GNOME_CDROM_DEVICE_TRANSIENT, error);
 }
+
+
+int
+msf2lba(SolarisCDRomTrackInfo *track_info)
+{
+
+        int     lba;
+
+        lba = track_info->address.minute * 60 * 75 +
+                track_info->address.second * 75 +
+                track_info->address.frame;
+
+        /* Go back 2 seconds */
+        lba -= 150;
+
+        return (lba);
+}
+
+
+void
+lba2msf(SolarisCDRom * cdp, int lba, struct cdrom_subchnl *subchnl)
+{
+        int             i;
+        div_t           lba_div_t;
+
+        /* Which track are we on? */
+        for (i = 0; i < cdp->priv->number_tracks; i++) {
+                if ((msf2lba(&cdp->priv->track_info[i]) <= lba) &&
+                    (lba < msf2lba(&cdp->priv->track_info[i + 1])))
+                        break;
+        }
+
+        /* Return if we're out of bounds */
+        if (i == cdp->priv->number_tracks) {
+                memset(subchnl, 0, sizeof (*subchnl));
+                return;
+        }
+
+        /* Track */
+        subchnl->cdsc_trk = i + 1;
+
+        /* Minute */
+        lba -= msf2lba(&cdp->priv->track_info[i]);
+
+        lba_div_t = div(lba, 75 * 60);
+
+        subchnl->cdsc_reladdr.msf.minute = lba_div_t.quot;
+
+        /* Second */
+        lba = lba_div_t.rem;
+
+        lba_div_t = div(lba, 75);
+
+        subchnl->cdsc_reladdr.msf.second = lba_div_t.quot;
+
+        /* Frame */
+        subchnl->cdsc_reladdr.msf.frame = lba_div_t.rem;
+
+        return;
+}
+	
--- /dev/null	2004-07-20 16:43:36.000000000 +0100
+++ gnome-media-2.6.1/gnome-cd/cdda-solaris.h	2004-07-20 18:35:38.454469000 +0100
@@ -0,0 +1,119 @@
+/*
+ * cdda-solaris.h
+ */
+#ifndef	__CDDA_SOLARIS_H__
+#define	__CDDA_SOLARIS_H__
+
+#include <sys/audio.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/cdio.h>
+#include <stdio.h>
+#include <stropts.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Basic CDDA read unit */
+#define	CDDA_BLKSZ		(2352)
+
+/*
+ * We define the CDDA read size to be 75 blocks (equating to 1s
+ * of audio data).
+ */
+#define	CDDA_CHUNK_BLKS		(75)
+
+/*
+ * CDDA data is queued up in memory. Here we define (in seconds)
+ * the amount of queued data.
+ */
+#define	CDDA_BUFFER_SECS	(2)
+
+/*
+ * During jitter correction we search through CDDA_SRCH_BLOCKS
+ * for a match.
+ */
+#define	CDDA_SRCH_BLKS		(2)
+
+/*
+ * In order to perform jitter correction we need to read more
+ * CDDA data than we "need". Below we define the amount of extra
+ * data required. So each CDDA read will comprise CDDA_CHUNK_BLKS
+ * + CDDA_OLAP_BLKS.
+ */
+#define	CDDA_OLAP_BLKS		(CDDA_SRCH_BLKS * 2)
+
+/*
+ * We have found a match when CDDA_STR_LEN bytes are identical in
+ * the data and the overlap.
+ */
+#define	CDDA_STR_LEN		(20)
+
+/* Buffer for audio data transfer */
+struct cdda_buffer {
+	pthread_mutex_t		mutex;		/* Mutex */
+	pthread_t		reader;		/* Thread reading from cd */
+	pthread_t		writer;		/* Thread writing to audio */
+	pthread_cond_t		more;		/* Wait for data */
+	pthread_cond_t		less;		/* Wait for room */
+	unsigned int		occupied;	/* Number of filled slots */
+	unsigned int		nextin;		/* Next free slot */
+	unsigned int		nextout;	/* Next out slot */
+	char			*olap;		/* Pointer to overlap */
+	char			*data;		/* Pointer to audio data */
+	char			*b;		/* The audio data */
+};
+
+typedef struct cdda_buffer cdda_buffer_t;
+
+/* Various sizes */
+struct cdda_size {
+	/* Chunk */
+	unsigned int	chunk_blocks;
+	unsigned int	chunk_bytes;
+
+	/* Buffer */
+	unsigned int	buffer_chunks;
+	unsigned int	buffer_blocks;
+	unsigned int	buffer_bytes;
+
+	/* Overlap */
+	unsigned int	olap_blocks;
+	unsigned int	olap_bytes;
+
+	/* Searching */
+	unsigned int	search_blocks;
+	unsigned int	search_bytes;
+	unsigned int	str_length;
+};
+
+typedef struct cdda_size cdda_size_t;
+
+/* Overall structure for cdda add on */
+struct cdda {
+	cdda_buffer_t	cdb;			/* Mutex protected data */
+	cdda_size_t	cds;			/* Various sizes stored here */
+	unsigned int	start_lba;		/* Start play position */
+	unsigned int	end_lba;		/* End play position */
+	int		cdrom;			/* Copy of CDROM fd */
+	int		audio;			/* Audio device fd */
+	char		supports_cdda;		/* Is CDDA supported? */
+	char		state;			/* Playing, paused, stopped */
+	char		*audio_device;		/* Name of audio device */
+};
+
+typedef struct cdda cdda_t;
+
+/* State */
+enum {
+	CDDA_PLAYING,
+	CDDA_STOPPED,
+	CDDA_PAUSED,
+	CDDA_COMPLETED
+};
+
+#endif /* __CDDA_SOLARIS_H__ */
--- /dev/null	2004-07-20 16:43:36.000000000 +0100
+++ gnome-media-2.6.1/gnome-cd/writer-solaris.h	2004-07-20 18:35:38.454603000 +0100
@@ -0,0 +1,16 @@
+/*
+ * writer-solaris.h
+ */
+#ifndef	__WRITER_SOLARIS_H__
+#define	__WRITER_SOLARIS_H__
+
+/* Exported function prototypes */
+int audio_start(cdda_t *, int, int);
+void audio_pause(cdda_t *);
+void audio_resume(cdda_t *);
+void audio_stop(cdda_t *);
+int audio_get_state(cdda_t *, struct cdrom_subchnl *);
+void cdda_check(void **, int);
+void cdda_cleanup(cdda_t *);
+
+#endif /* __WRITER_SOLARIS_H__ */
--- /dev/null	2004-07-20 16:43:36.000000000 +0100
+++ gnome-media-2.6.1/gnome-cd/writer-solaris.c	2004-07-20 18:35:38.454811000 +0100
@@ -0,0 +1,842 @@
+/*
+ * Code for writing CDDA data to the Solaris audio device. The
+ * audio_write thread retrieves data from a mutex protected
+ * buffer and sends it to the audio device.
+ */
+
+#include "cdda-solaris.h"
+#include "reader-solaris.h"
+#include "writer-solaris.h"
+#include <errno.h>
+
+/* Private function prototypes */
+static void audio_get_info(cdda_t *, audio_info_t *);
+static void audio_set_info(cdda_t *, audio_info_t *);
+static void audio_flush(cdda_t *);
+static void audio_drain(cdda_t *);
+static int cdda_audio_open(cdda_t *);
+static void cdda_audio_close(cdda_t *);
+static void audio_config(cdda_t *);
+static int audio_supports_analog(cdda_t *);
+static void audio_write_chunk(cdda_t *, char *);
+static void audio_write_eof(cdda_t *);
+static void *audio_write(void *);
+static void cdda_init(cdda_t *);
+
+/*
+ * audio_get_info()
+ *
+ * Description:
+ *	Wrapper for AUDIO_GETINFO ioctl.
+ *
+ * Arguments:
+ *	cdda_t			*cdda		Ptr to cdda state structure
+ *	audio_info_t		*info		Info to get
+ *
+ * Returns:
+ *	void
+ */
+static void
+audio_get_info(cdda_t *cdda, audio_info_t *info)
+{
+	/* Return if not open */
+	if (cdda->audio < 0) {
+		return;
+	}
+
+	/* Get */
+	if (ioctl(cdda->audio, AUDIO_GETINFO, info) < 0) {
+		perror("AUDIO_GETINFO");
+	}
+
+} /* audio_get_info() */
+
+
+/*
+ * audio_set_info()
+ *
+ * Description:
+ *	Wrapper for AUDIO_SETINFO ioctl.
+ *
+ * Arguments:
+ *	cdda_t			*cdda		Ptr to cdda state structure
+ *	audio_info_t		*info		Info to set
+ *
+ * Returns:
+ *	void
+ */
+static void
+audio_set_info(cdda_t *cdda, audio_info_t *info)
+{
+	/* Return if not open */
+	if (cdda->audio < 0) {
+		return;
+	}
+
+	/* Set */
+	if (ioctl(cdda->audio, AUDIO_SETINFO, info) < 0) {
+		perror("AUDIO_SETINFO");
+	}
+
+} /* audio_set_info() */
+
+
+/*
+ * audio_flush()
+ *
+ * Description:
+ *	Wrapper for STREAMS I_FLUSH ioctl.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+static void
+audio_flush(cdda_t *cdda)
+{
+	/* Return if not open */
+	if (cdda->audio < 0) {
+		return;
+	}
+
+	/* Flush */
+	if (ioctl(cdda->audio, I_FLUSH, FLUSHRW) < 0) {
+		perror("I_FLUSH");
+	}
+
+} /* audio_flush() */
+
+
+/*
+ * audio_drain()
+ *
+ * Description:
+ *	Wrapper for AUDIO_DRAIN ioctl.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+static void
+audio_drain(cdda_t *cdda)
+{
+	/* Return if not open */
+	if (cdda->audio < 0) {
+		return;
+	}
+
+	/* Drain */
+	if (ioctl(cdda->audio, AUDIO_DRAIN, NULL) < 0) {
+		perror("AUDIO_DRAIN");
+	}
+
+} /* audio_drain() */
+
+
+/*
+ * cdda_audio_open()
+ *
+ * Description:
+ *	Opens the audio device.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	int: The error number if open fails
+ */
+static int
+cdda_audio_open(cdda_t *cdda)
+{
+	extern int errno;
+	errno = 0;
+	/* Return if already open */
+	if (cdda->audio >= 0) {
+		return errno;
+	}
+
+	/* Open audio device */
+	if ((cdda->audio = open(cdda->audio_device, O_WRONLY | O_NDELAY)) < 0) {
+		perror("open()");
+		return errno;
+	}
+
+	fcntl (cdda->audio, F_SETFL, O_WRONLY);
+	return errno;
+} /* cdda_audio_open() */
+
+
+/*
+ * cdda_audio_close()
+ *
+ * Description:
+ *	Closes the audio device.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+static void
+cdda_audio_close(cdda_t *cdda)
+{
+	/* Return if already closed */
+	if (cdda->audio < 0) {
+		return;
+	}
+
+	/* Close audio device */
+	if (close(cdda->audio) < 0) {
+		perror("close()");
+	} else {
+		cdda->audio = -1;
+	}
+
+} /* cdda_audio_close() */
+
+
+/*
+ * audio_config()
+ *
+ * Description:
+ *	Sets up audio device for playing cd quality audio.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+static void
+audio_config(cdda_t *cdda)
+{
+	audio_info_t	audio_info;
+
+	/* Initialise */
+	AUDIO_INIT(&audio_info, sizeof (audio_info));
+
+	/* Configure for 44.1kHz 16-bit stereo */
+	audio_info.play.sample_rate = 44100;
+	audio_info.play.channels = 2;
+	audio_info.play.precision = 16;
+	audio_info.play.encoding = AUDIO_ENCODING_LINEAR;
+
+	/* Apply new settings */
+	audio_set_info(cdda, &audio_info);
+
+} /* audio_config() */
+
+
+/*
+ * audio_supports_analog()
+ *
+ * Description:
+ *	Returns 1 if input ports contains AUDIO_CD, zero otherwise.
+ *	If AUDIO_CD available then the monitor gain and cd volume
+ *	are set to their maxmimum values to ensure output is audible.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state stucture
+ *
+ * Returns:
+ *	0		AUDIO_CD unavailable
+ *	1		AUDIO_CD available
+ */
+static int
+audio_supports_analog(cdda_t *cdda)
+{
+	audio_info_t		audio_info;
+	struct cdrom_volctrl	volctrl;
+
+	/* Retrieve info */
+	audio_get_info(cdda, &audio_info);
+
+	/* Check for AUDIO_CD */
+	if (audio_info.record.avail_ports & AUDIO_CD) {
+
+		/* Initialise */
+		AUDIO_INIT(&audio_info, sizeof (audio_info));
+
+		/* Set the port to AUDIO_CD */
+		audio_info.record.port = AUDIO_CD;
+
+		/* Set monitor gain */
+		audio_info.monitor_gain = AUDIO_MAX_GAIN;
+
+		/* Apply */
+		audio_set_info(cdda, &audio_info);
+
+		/* Configure for CD audio */
+		audio_config(cdda);
+
+		/* Set CD volume to max */
+		volctrl.channel0 = AUDIO_MAX_GAIN;
+		volctrl.channel1 = AUDIO_MAX_GAIN;
+
+		/* Apply */
+		if (ioctl(cdda->cdrom, CDROMVOLCTRL, &volctrl) < 0) {
+			perror("CDROMVOLCTRL");
+		}
+
+		/* Return success */
+		return (1);
+	}
+
+	/* Return failure */
+	return (0);
+
+} /* audio_supports_analog() */
+
+
+/*
+ * audio_pause()
+ *
+ * Description:
+ *	Pause audio output.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+void
+audio_pause(cdda_t *cdda)
+{
+	audio_info_t		audio_info;
+
+	/* If not playing do nothing */
+	if (cdda->state == CDDA_STOPPED) {
+		return;
+	}
+
+	/* Initialise */
+	AUDIO_INIT(&audio_info, sizeof (audio_info));
+
+	/* Turn on pause */
+	audio_info.play.pause = 1;
+
+	/* Apply */
+	audio_set_info(cdda, &audio_info);
+
+	/* Set paused flag */
+	cdda->state = CDDA_PAUSED;
+
+} /* audio_pause() */
+
+
+/*
+ * audio_resume()
+ *
+ * Description:
+ *	Resume audio output.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+void
+audio_resume(cdda_t *cdda)
+{
+	audio_info_t		audio_info;
+
+	/* If not paused do nothing */
+	if (cdda->state != CDDA_PAUSED) {
+		return;
+	}
+
+	/* Initialise */
+	AUDIO_INIT(&audio_info, sizeof (audio_info));
+
+	/* Turn off pause */
+	audio_info.play.pause = 0;
+
+	/* Apply */
+	audio_set_info(cdda, &audio_info);
+
+	/* Clear paused flag */
+	cdda->state = CDDA_PLAYING;
+
+} /* audio_resume() */
+
+
+/*
+ * audio_write_chunk()
+ *
+ * Description:
+ *	Writes data to the device.
+ *
+ * Arguments:
+ *	cdda_t			*cdda			Ptr to cdda state
+ *	char			*data			Data to write
+ *
+ * Returns:
+ *	void
+ */
+static void
+audio_write_chunk(cdda_t *cdda, char *data)
+{
+
+	if (write(cdda->audio, data, cdda->cds.chunk_bytes) <
+			cdda->cds.chunk_bytes) {
+		perror("write()");
+	}
+
+} /* audio_write_chunk() */
+
+
+/*
+ * audio_write_eof()
+ *
+ * Description:
+ *	Puts an eof marker in the audio stream in order to keep track
+ *	of where we are on the cd.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+static void
+audio_write_eof(cdda_t *cdda)
+{
+	if (write(cdda->audio, 0, 0) < 0) {
+		perror("write()");
+	}
+
+} /* audio_write_eof() */
+
+
+/*
+ * audio_stop()
+ *
+ * Description:
+ *	Shuts down reader/writer threads and closes the audio device.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+void
+audio_stop(cdda_t *cdda)
+{
+	/* If stopped do nothing */
+	if (cdda->state == CDDA_STOPPED) {
+		return;
+	}
+
+	/* May be paused, in which case clear the paused flag */
+	if (cdda->state == CDDA_PAUSED) {
+		audio_resume(cdda);
+	}
+
+	/* Set status */
+	cdda->state = CDDA_STOPPED;
+
+	/* Wait for threads */
+	if (pthread_join(cdda->cdb.writer, NULL) < 0) {
+		perror("pthread_join()");
+	}
+
+	if (pthread_join(cdda->cdb.reader, NULL) < 0) {
+		perror("pthread_join()");
+	}
+
+	/* Flush the device */
+	audio_flush(cdda);
+
+	/* Close the device */
+	cdda_audio_close(cdda);
+
+} /* audio_stop() */
+
+
+/*
+ * audio_write()
+ *
+ * Description:
+ *	Thread respsonsible for writing from the buffer to the audio
+ *	device. Each chunk is followed by an eof marker.
+ *
+ * Arguments:
+ *	void		*in_cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+static void *
+audio_write(void *in_cdda)
+{
+	char		*data;
+	cdda_t		*cdda;
+
+	/* Cast */
+	cdda = (cdda_t *)in_cdda;
+
+	/* Get some memory */
+	data = malloc(cdda->cds.chunk_bytes);
+
+	if (data == NULL) {
+		perror("malloc()");
+		exit(EXIT_FAILURE);
+	}
+
+	/* While not stopped */
+	while (cdda->state != CDDA_STOPPED) {
+
+		/* Get lock */
+		(void) pthread_mutex_lock(&cdda->cdb.mutex);
+
+		/* End if finished and nothing in the buffer */
+		if ((cdda->cdb.occupied == 0) &&
+				(cdda->state == CDDA_COMPLETED)) {
+			audio_drain(cdda);
+			cdda->state = CDDA_STOPPED;
+			cdda_audio_close(cdda);
+			(void) pthread_mutex_unlock(&cdda->cdb.mutex);
+			break;
+		}
+
+		/* Wait until there is something there */
+		while ((cdda->cdb.occupied == 0) &&
+				(cdda->state != CDDA_STOPPED)) {
+			(void) pthread_cond_wait(&cdda->cdb.more,
+					&cdda->cdb.mutex);
+		}
+
+		/* Break if stopped */
+		if (cdda->state == CDDA_STOPPED) {
+			(void) pthread_mutex_unlock(&cdda->cdb.mutex);
+			break;
+		}
+
+		/* Read a chunk from the nextout slot */
+		(void) memcpy(data, &cdda->cdb.b[cdda->cds.chunk_bytes *
+			cdda->cdb.nextout], cdda->cds.chunk_bytes);
+
+		/* Update pointers */
+		cdda->cdb.nextout++;
+		cdda->cdb.nextout %= cdda->cds.buffer_chunks;
+		cdda->cdb.occupied--;
+
+		/* Release lock and signal room available */
+		(void) pthread_cond_signal(&cdda->cdb.less);
+		(void) pthread_mutex_unlock(&cdda->cdb.mutex);
+
+		/* Write to device */
+		audio_write_chunk(cdda, data);
+
+		/* Write eof marker */
+		audio_write_eof(cdda);
+	}
+
+	/* Wake up reader */
+	(void) pthread_cond_signal(&cdda->cdb.less);
+
+	/* Free memory */
+	free(data);
+
+	return (NULL);
+
+} /* audio_write() */
+
+
+/*
+ * audio_get_state()
+ *
+ * Description:
+ *	Fills in the subchannel information, based on the current eof
+ *	count. The returned status is one of CDROM_AUDIO_NOSTATUS,
+ *	CDROM_AUDIO_PLAY or CDROM_AUDIO_PAUSED.
+ *
+ * Arguments:
+ *	cdda_t			*cdda		Ptr to cdda state structure
+ *	struct cdrom_subchnl	*subchnl	Subchannel information
+ *
+ * Returns:
+ *	int			lba		Current logical block addr
+ */
+int
+audio_get_state(cdda_t *cdda, struct cdrom_subchnl *subchnl)
+{
+	audio_info_t		audio_info;
+	int			curr_lba;
+
+	/* Just return if not playing */
+	if (cdda->state == CDDA_STOPPED) {
+		subchnl->cdsc_audiostatus = CDROM_AUDIO_NO_STATUS;
+		return (0);
+	}
+
+	/* Retrieve current setting */
+	audio_get_info(cdda, &audio_info);
+
+	/* Get current lba */
+	curr_lba = cdda->start_lba + audio_info.play.eof * 75;
+
+	if (cdda->state == CDDA_PAUSED) {
+		subchnl->cdsc_audiostatus = CDROM_AUDIO_PAUSED;
+	} else {
+		/*
+		 * The state must either be CDDA_PLAYING or CDDA_COMPLETED
+		 * in which case we are still playing.
+		 */
+		subchnl->cdsc_audiostatus = CDROM_AUDIO_PLAY;
+	}
+
+	/* Return lba and let caller figure out track no */
+	return (curr_lba);
+
+} /* audio_get_state() */
+
+
+/*
+ * audio_start()
+ *
+ * Description:
+ *	Opens audio device and launches reader/writer threads.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state structure
+ *	int		start_lba	Start play address
+ *	int		end_lba		End play address
+ *
+ * Returns:
+ *	int: -1 for failure and 1 for sucess 
+ */
+int
+audio_start(cdda_t *cdda, int start_lba, int end_lba)
+{
+	int errno;
+	/* If playing do nothing */
+	if (cdda->state != CDDA_STOPPED) {
+		return 1;
+	}
+
+	/* Initialise */
+	cdda->start_lba = start_lba;
+	cdda->end_lba = end_lba;
+
+	/* Buffer variables */
+	cdda->cdb.occupied = 0;
+	cdda->cdb.nextin = 0;
+	cdda->cdb.nextout = 0;
+
+	/* Open the audio device */
+	errno = cdda_audio_open(cdda);
+	if (cdda->audio < 0 && errno == EBUSY)
+		return -1;
+
+	/* Configure for CD audio */
+	audio_config(cdda);
+
+	/* Fill buffers */
+	cdda_read_init(cdda);
+
+	/* Set status */
+	cdda->state = CDDA_PLAYING;
+
+	/* Start reading */
+	if (pthread_create(&cdda->cdb.reader, NULL, cdda_read,
+				(void *)cdda) < 0) {
+		perror("pthread_create()");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Start writing */
+	if (pthread_create(&cdda->cdb.writer, NULL, audio_write,
+				(void *)cdda) < 0) {
+		perror("pthread_create()");
+		exit(EXIT_FAILURE);
+	}
+
+	return 1;
+} /* audio_start() */
+
+
+/*
+ * cdda_cleanup()
+ *
+ * Description:
+ *	Frees memory and destroys mutexes and condition variables.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+void
+cdda_cleanup(cdda_t *cdda)
+{
+	/* Free memory and destroy mutexes, condition variables */
+	free(cdda->audio_device);
+	pthread_mutex_destroy(&cdda->cdb.mutex);
+	pthread_cond_destroy(&cdda->cdb.more);
+	pthread_cond_destroy(&cdda->cdb.less);
+	free(cdda->cdb.b);
+	free(cdda->cdb.data);
+	free(cdda->cdb.olap);
+	free(cdda);
+	cdda = NULL;
+
+} /* cdda_cleanup() */
+
+
+/*
+ * cdda_check()
+ *
+ * Description:
+ *	Checks whether analog playback is supported. If analog playback
+ *	is not supported then sets up for cdda playback.
+ *
+ * Arguments:
+ *	void		*in_cdda		Ptr to uninitialised structure
+ *	int		cdrom			CDROM file descriptor
+ *
+ * Returns:
+ *	void
+ */
+void
+cdda_check(void **in_cdda, int cdrom)
+{
+	cdda_t	*cdda;
+
+	/* Allocate cdda structure */
+	cdda = malloc(sizeof (*cdda));
+
+	if (cdda == NULL) {
+		perror("malloc()");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Retrieve AUDIODEV, if set */
+	if (getenv("AUDIODEV") != NULL) {
+		cdda->audio_device = strdup(getenv("AUDIODEV"));
+	} else {
+		cdda->audio_device = strdup("/dev/audio");
+	}
+	if (cdda->audio_device == NULL) {
+		perror("strdup()");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Initialise fds */
+	cdda->audio = -1;
+	cdda->cdrom = cdrom;
+
+	/* Open the device */
+	cdda_audio_open(cdda);
+
+	/* Check for analog support */
+	if (audio_supports_analog(cdda)) {
+		/* Clean up */
+		cdda_audio_close(cdda);
+		free(cdda->audio_device);
+		free(cdda);
+		*in_cdda = NULL;
+		return;
+	}
+
+	/*
+	 * Analog playback is not supported so we can go ahead
+	 * and initialise the cdda structure.
+	 */
+	cdda_audio_close(cdda);
+	cdda_init(cdda);
+	*in_cdda = cdda;
+
+} /* cdda_check() */
+
+
+/*
+ * cdda_init()
+ *
+ * Description:
+ *	Sets up mutexes and condition variables and allocates memory
+ *	required for cdda.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+static void
+cdda_init(cdda_t *cdda)
+{
+	/* Initialise mutex and condition variables */
+	if (pthread_mutex_init(&cdda->cdb.mutex, NULL) < 0) {
+		perror("pthread_mutex_init()");
+		exit(EXIT_FAILURE);
+	}
+	if (pthread_cond_init(&cdda->cdb.more, NULL) < 0) {
+		perror("pthread_cond_init()");
+		exit(EXIT_FAILURE);
+	}
+	if (pthread_cond_init(&cdda->cdb.less, NULL) < 0) {
+		perror("pthread_cond_init()");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Chunk blocks, bytes */
+	cdda->cds.chunk_blocks = CDDA_CHUNK_BLKS;
+	cdda->cds.chunk_bytes = cdda->cds.chunk_blocks * CDDA_BLKSZ;
+
+	/* Buffer chunks, blocks, bytes */
+	cdda->cds.buffer_chunks = CDDA_BUFFER_SECS;
+	cdda->cds.buffer_blocks = cdda->cds.buffer_chunks *
+		cdda->cds.chunk_blocks;
+	cdda->cds.buffer_bytes = cdda->cds.buffer_blocks *
+		cdda->cds.chunk_bytes;
+
+	/* Overlap block, bytes */
+	cdda->cds.olap_blocks = CDDA_OLAP_BLKS;
+	cdda->cds.olap_bytes = cdda->cds.olap_blocks * CDDA_BLKSZ;
+
+	/* Search blocks, bytes */
+	cdda->cds.search_blocks = CDDA_SRCH_BLKS;
+	cdda->cds.search_bytes = cdda->cds.search_blocks * CDDA_BLKSZ;
+
+	/* Jitter correction match length */
+	cdda->cds.str_length = CDDA_STR_LEN;
+
+	/* Allocate space for data */
+	cdda->cdb.b = malloc(cdda->cds.buffer_bytes);
+
+	if (cdda->cdb.b == NULL) {
+		perror("malloc()");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Allocate space for read */
+	cdda->cdb.data = malloc(cdda->cds.chunk_bytes +
+		(cdda->cds.olap_bytes << 1));
+
+	if (cdda->cdb.data == NULL) {
+		perror("malloc()");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Allocate space for overlap */
+	cdda->cdb.olap = malloc(cdda->cds.search_bytes << 1);
+
+	if (cdda->cdb.olap == NULL) {
+		perror("malloc()");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Set state */
+	cdda->state = CDDA_STOPPED;
+
+} /* cdda_init() */
--- /dev/null	2004-07-20 16:43:36.000000000 +0100
+++ gnome-media-2.6.1/gnome-cd/reader-solaris.h	2004-07-20 18:35:38.454987000 +0100
@@ -0,0 +1,11 @@
+/*
+ * reader-solaris.h
+ */
+#ifndef	__READER_SOLARIS_H__
+#define	__READER_SOLARIS_H__
+
+/* Exported function prototypes */
+void *cdda_read(void *);
+void cdda_read_init(cdda_t *);
+
+#endif /* __READER_SOLARIS_H__ */
--- /dev/null	2004-07-20 16:43:36.000000000 +0100
+++ gnome-media-2.6.1/gnome-cd/reader-solaris.c	2004-07-20 18:35:38.455140000 +0100
@@ -0,0 +1,326 @@
+/*
+ * Code for reading CDDA data from the CD. The cdda_read thread reads
+ * data from the CD, bytes swaps if necessary, corrects for jitter and
+ * places the audio data in a mutex protected buffer.
+ */
+
+#include "cdda-solaris.h"
+#include "reader-solaris.h"
+
+/* Private function prototypes */
+static void cdda_byte_swap(cdda_t *);
+static char *cdda_correct_jitter(cdda_t *);
+
+/*
+ * cdda_byte_swap()
+ *
+ * Description:
+ *	Carry out byte swapping. Data coming off the cd is little endian
+ *	and so byte swapping is necessary on Sparc.
+ *
+ * Arguments:
+ *	cdda_t			*cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+static void
+cdda_byte_swap(cdda_t *cdda)
+{
+	unsigned int	i;
+	char		temp;
+
+	/* Run through samples */
+	for (i = cdda->cds.olap_bytes; i < cdda->cds.chunk_bytes +
+			(cdda->cds.olap_bytes << 1); i += 2) {
+		/* Byte swapping */
+		temp = cdda->cdb.data[i + 1];
+		cdda->cdb.data[i + 1] = cdda->cdb.data[i];
+		cdda->cdb.data[i] = temp;
+	}
+
+} /* cdda_byte_swap() */
+
+
+/*
+ * cdda_correct_jitter()
+ *
+ * Description:
+ *	Correct for jitter. The olap array contains 2 * cdda->cds.search_bytes
+ *	of data and the last data written to the device is situated at
+ *	olap[cdda->cds.search_bytes]. We try to match the last data read,
+ *	which starts at data[cdda->cds.olap_bytes], around the center of olap.
+ *	Once matched we may have to copy some data from olap into data
+ *	(overshoot) or skip some of the audio in data (undershoot). The
+ *	function returns a pointer into data indicating from where we should
+ *	start writing.
+ *
+ * Arguments:
+ *	cdda_t		*cdda			Ptr to cdda state structure
+ *
+ * Returns:
+ *	char		*j			Pointer into data
+ */
+static char *
+cdda_correct_jitter(cdda_t *cdda)
+{
+	int		index, d;
+	char		*i, *j, *l;
+
+	/* Find a match in olap, if we can */
+	i = cdda->cdb.olap;
+	l = &cdda->cdb.olap[(cdda->cds.search_bytes << 1) - 1];
+	j = &cdda->cdb.data[cdda->cds.olap_bytes];
+
+	do {
+		i = memchr(i, (int)*j, l - i);
+		if (i != NULL) {
+			/* Make sure we're at a sample boundary */
+			if ((i - cdda->cdb.olap) % 2 == 0) {
+				d = memcmp(j, i, cdda->cds.str_length);
+				if (d == 0)
+					break;
+			}
+			i++;
+		}
+	} while ((i != NULL) && (i < l));
+
+	/* Did we find a match? */
+	if ((i == NULL) || (i >= l)) {
+		(void) fprintf(stderr, "Could not correct for jitter\n");
+		index = 0;
+	} else {
+		index = i - cdda->cdb.olap - cdda->cds.search_bytes;
+	}
+
+	/* Update pointer */
+	j -= index;
+
+	/* Match on RHS, copy some olap into data */
+	if (index > 0) {
+		(void) memcpy(j, cdda->cdb.olap + cdda->cds.search_bytes,
+			index);
+	}
+
+	return (j);
+
+} /* cdda_correct_jitter() */
+
+
+/*
+ * cdda_read_init()
+ *
+ * Description:
+ *	Before beginning to play a track we fill the data buffer.
+ *
+ * Arguments:
+ *	cdda_t		*cdda		Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+void
+cdda_read_init(cdda_t *cdda)
+{
+	struct cdrom_cdda	cd_cdda;
+	unsigned int		i;
+	char			*start, *end, *p;
+
+	/* Initialise */
+	start = &cdda->cdb.data[cdda->cds.olap_bytes];
+
+	/* Set up for read */
+	cd_cdda.cdda_addr = cdda->start_lba;
+	cd_cdda.cdda_length = cdda->cds.chunk_blocks + cdda->cds.olap_blocks;
+	cd_cdda.cdda_subcode = CDROM_DA_NO_SUBCODE;
+	cd_cdda.cdda_data = (caddr_t)start;
+
+	/* Fill up our buffer */
+	i = 0;
+
+	while ((i < cdda->cds.buffer_chunks) &&
+			(cdda->state != CDDA_COMPLETED)) {
+
+		/*
+		 * If we are passing the end of the track we have to take
+		 * special action. We read in up to end_lba and zero out
+		 * the buffer. This is equivalent to rounding to the nearest
+		 * cdda->cds.chunk_bytes. We also set the COMPLETED flag.
+		 */
+		if (cd_cdda.cdda_addr + cdda->cds.chunk_blocks >=
+				cdda->end_lba) {
+			/* Read up to end */
+			if (cdda->end_lba - cd_cdda.cdda_addr > 0) {
+				cd_cdda.cdda_length = cdda->end_lba -
+					cd_cdda.cdda_addr;
+			} else {
+				cd_cdda.cdda_length = 0;
+			}
+			(void) memset(cdda->cdb.data, 0, cdda->cds.chunk_bytes +
+				(cdda->cds.olap_bytes << 1));
+			cdda->state = CDDA_COMPLETED;
+		}
+
+		/* Read audio from cd */
+		if (ioctl(cdda->cdrom, CDROMCDDA, &cd_cdda) < 0) {
+			/* Print error, don't exit as not critical */
+			perror("CDROMCDDA");
+		}
+
+		/* Do byte swapping */
+#if defined(sparc) || defined(__sparc)
+		cdda_byte_swap(cdda);
+#endif
+
+		/* Do jitter correction */
+		if (i == 0) {
+			p = &cdda->cdb.data[cdda->cds.olap_bytes];
+		} else {
+			p = cdda_correct_jitter(cdda);
+		}
+
+		/* Data end */
+		end = p + cdda->cds.chunk_bytes;
+
+		/* Reinitialise olap */
+		(void) memcpy(cdda->cdb.olap, end - cdda->cds.search_bytes,
+			cdda->cds.search_bytes << 1);
+
+		/* Copy in */
+		(void) memcpy(&cdda->cdb.b[cdda->cds.chunk_bytes *
+			cdda->cdb.nextin], p, cdda->cds.chunk_bytes);
+
+		/* Update pointers */
+		cdda->cdb.nextin++;
+		cdda->cdb.nextin %= cdda->cds.buffer_chunks;
+		cdda->cdb.occupied++;
+		cd_cdda.cdda_addr += ((end - start) / CDDA_BLKSZ);
+
+		/* Check if we need to round up */
+		if (((end - start) % CDDA_BLKSZ) >= (CDDA_BLKSZ >> 1))
+			cd_cdda.cdda_addr++;
+
+		i++;
+	}
+
+} /* cdda_read_init() */
+
+
+/*
+ * cdda_read()
+ *
+ * Description:
+ *	Thread respsonsible for reading from the cd, byte swapping,
+ *	correcting for jitter and writing to the buffer.
+ *
+ * Arguments:
+ *	void			*in_cdda	Ptr to cdda state structure
+ *
+ * Returns:
+ *	void
+ */
+void *
+cdda_read(void *in_cd)
+{
+	cdda_t			*cdda;
+	struct cdrom_cdda	cd_cdda;
+	char			*start, *end, *p;
+
+	/* Cast */
+	cdda = (cdda_t *)in_cd;
+
+	/* Initialise */
+	start = &cdda->cdb.data[cdda->cds.olap_bytes];
+
+	/* Set up for read */
+	cd_cdda.cdda_addr = cdda->start_lba + cdda->cds.buffer_blocks;
+	cd_cdda.cdda_length = cdda->cds.chunk_blocks + cdda->cds.olap_blocks;
+	cd_cdda.cdda_data = (caddr_t)start;
+	cd_cdda.cdda_subcode = CDROM_DA_NO_SUBCODE;
+
+	/* While not stopped or completed */
+	while ((cdda->state != CDDA_STOPPED) &&
+			(cdda->state != CDDA_COMPLETED)) {
+
+		/*
+		 * If we are passing the end of the track we have to take
+		 * special action. We read in up to end_lba and zero out
+		 * the buffer. This is equivalent to rounding to the nearest
+		 * cdda->cds.chunk_bytes. We also set the COMPLETED flag.
+		 */
+		if (cd_cdda.cdda_addr + cdda->cds.chunk_blocks >=
+				cdda->end_lba) {
+			/* Read up to end */
+			if (cdda->end_lba - cd_cdda.cdda_addr > 0) {
+				cd_cdda.cdda_length = cdda->end_lba -
+					cd_cdda.cdda_addr;
+			} else {
+				cd_cdda.cdda_length = 0;
+			}
+			(void) memset(cdda->cdb.data, 0, cdda->cds.chunk_bytes +
+				(cdda->cds.olap_bytes << 1));
+			cdda->state = CDDA_COMPLETED;
+		}
+
+		/* Read audio from cd using ioctl method */
+		if (ioctl(cdda->cdrom, CDROMCDDA, &cd_cdda) < 0) {
+			/* Print error, don't exit as not critical */
+			perror("CDROMCDDA");
+		}
+
+		/* Do byte swapping */
+#if defined(sparc) || defined(__sparc)
+		cdda_byte_swap(cdda);
+#endif
+
+		/* Do jitter correction */
+		p = cdda_correct_jitter(cdda);
+
+		/* Data end */
+		end = p + cdda->cds.chunk_bytes;
+
+		/* Reinitialise cd_olap */
+		(void) memcpy(cdda->cdb.olap, end - cdda->cds.search_bytes,
+			cdda->cds.search_bytes << 1);
+
+		/* Get lock */
+		(void) pthread_mutex_lock(&cdda->cdb.mutex);
+
+		/* Wait until there is room */
+		while ((cdda->cdb.occupied >= cdda->cds.buffer_chunks) &&
+			(cdda->state != CDDA_STOPPED)) {
+				(void) pthread_cond_wait(&cdda->cdb.less,
+					&cdda->cdb.mutex);
+		}
+
+		/* Break if stopped */
+		if (cdda->state == CDDA_STOPPED) {
+			(void) pthread_mutex_unlock(&cdda->cdb.mutex);
+			break;
+		}
+
+		/* Copy in */
+		(void) memcpy(&cdda->cdb.b[cdda->cds.chunk_bytes *
+			cdda->cdb.nextin], p, cdda->cds.chunk_bytes);
+
+		/* Update pointers */
+		cdda->cdb.nextin++;
+		cdda->cdb.nextin %= cdda->cds.buffer_chunks;
+		cdda->cdb.occupied++;
+		cd_cdda.cdda_addr += ((end - start) / CDDA_BLKSZ);
+
+		/* Check if we need to round up */
+		if (((end - start) % CDDA_BLKSZ) >= (CDDA_BLKSZ >> 1))
+			cd_cdda.cdda_addr++;
+
+		/* Release lock and signal data available */
+		(void) pthread_cond_signal(&cdda->cdb.more);
+		(void) pthread_mutex_unlock(&cdda->cdb.mutex);
+	}
+
+	/* Wake up writer */
+	(void) pthread_cond_signal(&cdda->cdb.more);
+
+	return (NULL);
+
+} /* cdda_read() */