--- 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() */