2004-07-23 Balamurali Viswanathan <[email protected]>
authorbviswa
Fri, 23 Jul 2004 14:02:21 +0000
changeset 3410 5cb45623e963
parent 3409 6b1bc521fbb7
child 3411 6f26d598a8b3
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
ChangeLog
gnome-media.spec
patches/gnome-media-09-cdda.diff
--- a/ChangeLog	Fri Jul 23 13:31:02 2004 +0000
+++ b/ChangeLog	Fri Jul 23 14:02:21 2004 +0000
@@ -1,3 +1,12 @@
+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
+
 2004-07-23  Vinay M R  <[email protected]>
 
 	* sun-gdm-themes.spec: tarball upgrade
--- a/gnome-media.spec	Fri Jul 23 13:31:02 2004 +0000
+++ b/gnome-media.spec	Fri Jul 23 14:02:21 2004 +0000
@@ -2,7 +2,7 @@
 Summary:      GNOME Multimedia
 Group:        System/GUI/GNOME
 Version:      2.6.1
-Release:      16
+Release:      17
 Distribution: Cinnabar
 Vendor:       Sun Microsystems, Inc.
 License:      GPL
@@ -21,6 +21,7 @@
 Patch6:       gnome-media-06-destroy-cache.diff
 Patch7:       gnome-media-07-set-audio-port.diff
 Patch8:       gnome-media-08-g11n-configure.diff
+Patch9:       gnome-media-09-cdda.diff
 URL:          http://www.gnome.org
 BuildRoot:    %{_tmppath}/%{name}-%{version}-build
 Docdir:       %{_docdir}
@@ -58,6 +59,7 @@
 %patch6 -p1
 %patch7 -p1
 %patch8 -p1
+%patch9 -p1
 
 bzcat %SOURCE1 | tar xf -
 bzcat %SOURCE2 | tar xf -
@@ -181,6 +183,10 @@
 %{_mandir}/man1/*
 
 %changelog
+* Fri Jul 23 2004 - [email protected]
+- Patch to make gnome-cd play in machines where there is no audio cable.
+  Fixes bug #5061178
+
 * Thu Jul 08 2004 - [email protected]
 - Updated l10n content to gnome-media-l10n-po-1.2.tar.bz2
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/patches/gnome-media-09-cdda.diff	Fri Jul 23 14:02:21 2004 +0000
@@ -0,0 +1,1668 @@
+--- 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() */