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