Re: [Rhythmbox-devel] audiocd view in (net)rhythmbox



On Tue, 2003-04-26 at 16:53, Jorn Baayen wrote:

> As ever, I want to see a patch, then I can see what can go in and what
> cannot.. I have no overview on what is changed now.

Ok, I diffed the two trees.  The attached patch has a few spurious bits
that would need to be fixed if it was going to be applied to the
monkey-media tree, but it should give you an overview of what was
changed.

 
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/Makefile.am net-rhythmbox/monkey-media/Makefile.am
--- monkey-media/src/Makefile.am	2003-01-24 11:07:32.000000000 -0500
+++ net-rhythmbox/monkey-media/Makefile.am	2003-04-25 03:12:46.000000000 -0400
@@ -1,38 +1,43 @@
 SUBDIRS = stream-info-impl
 
-lib_LTLIBRARIES = libmonkey-media.la
+noinst_LTLIBRARIES = libmonkey-media.la
 
 BUILT_SOURCES =						\
 	monkey-media-marshal.c				\
 	monkey-media-marshal.h
 
-libmonkey_mediadir = $(includedir)/monkey-media
-
-libmonkey_media_HEADERS =				\
-	monkey-media.h					\
-	monkey-media-stream-info.h			\
-	monkey-media-player.h				\
-	monkey-media-audio-cd.h				\
-	monkey-media-audio-quality.h
-
 libmonkey_media_la_SOURCES =				\
 	monkey-media.c					\
+	monkey-media-debug.c				\
+	monkey-media-debug.h				\
 	monkey-media-stream-info.c			\
 	monkey-media-player-xine.c			\
 	monkey-media-player-gst.c			\
 	monkey-media-audio-quality.c			\
-	monkey-media-audio-cd.c				\
-	monkey-media-audio-cd-private.h			\
 	monkey-media-private.h				\
 	sha1.c						\
 	sha1.h						\
 	monkey-media-musicbrainz.c			\
 	monkey-media-musicbrainz.h			\
+	monkey-media.h					\
+	monkey-media-stream-info.h			\
+	monkey-media-player.h				\
+	monkey-media-audio-quality.h			\
 	$(BUILT_SOURCES)
 
+if AUDIOCD
+libmonkey_media_la_SOURCES += 	monkey-media-audio-cd.c				\
+				monkey-media-audio-cd.h				\
+				monkey-media-audio-cd-private.h
+endif
+
+if LINUX_CDROM
+libmonkey_media_la_SOURCES += monkey-media-audio-cd-linux.c
+endif
+
 libmonkey_media_la_LIBADD = 					    \
-	$(top_builddir)/src/stream-info-impl/libstream-info-impl.la \
-	$(MONKEYMEDIA_LIBS)
+	$(top_builddir)/monkey-media/stream-info-impl/libstream-info-impl.la \
+	$(RHYTHMBOX_LIBS)
 
 libmonkey_media_la_LDFLAGS = -export-dynamic
 
@@ -40,8 +45,8 @@
         -DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
 	-DG_LOG_DOMAIN=\"MonkeyMedia\"			\
 	-I$(top_srcdir) 				\
-	-I$(top_srcdir)/src/stream-info-impl		\
-	$(MONKEYMEDIA_CFLAGS)
+	-I$(top_srcdir)/monkey-media/stream-info-impl		\
+	$(RHYTHMBOX_CFLAGS)
 
 monkey-media-marshal.h: monkey-media-marshal.list $(GLIB_GENMARSHAL)
 	$(GLIB_GENMARSHAL) $< --header --prefix=monkey_media_marshal > $@
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/monkey-media-audio-cd.c net-rhythmbox/monkey-media/monkey-media-audio-cd.c
--- monkey-media/src/monkey-media-audio-cd.c	2002-11-17 18:45:08.000000000 -0500
+++ net-rhythmbox/monkey-media/monkey-media-audio-cd.c	2003-04-26 16:53:41.000000000 -0400
@@ -3,6 +3,7 @@
  *                2002 Kenneth Christiansen <kenneth@gnu.org>
  *                     Olivier Martin <omartin@ifrance.com>
  *                     Jorn Baayen <jorn@nl.linux.org>
+ *                2003 Colin Walters <walters@verbum.org>
  *  
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -32,10 +33,13 @@
 #include <time.h>
 #include <stdlib.h>
 #include <string.h>
+#if HAVE_LINUX_CDROM_H
 #include <linux/cdrom.h>
+#endif
 
 #include "monkey-media.h"
 #include "monkey-media-private.h"
+#include "monkey-media-audio-cd-private.h"
 #include "sha1.h"
 
 static void monkey_media_audio_cd_class_init (MonkeyMediaAudioCDClass *klass);
@@ -51,40 +55,6 @@
 				                GParamSpec *pspec);
 static gboolean poll_event_cb (MonkeyMediaAudioCD *cd);
       
-struct MonkeyMediaAudioCDPrivate 
-{
-	int open_count;
-
-        int poll_func_id;
-
-	GError *error;
-
-	int fd;
-
-        gboolean valid_info;
-
-	gboolean cd_available;
-	
-        int n_audio_tracks;
-        int *track_lengths;
-        int *track_offsets;
-
-	char *cd_id;
-
-	GMutex *lock;
-};
-
-enum
-{
-	PROP_0,
-	PROP_ERROR
-};
-
-enum
-{
-	CD_CHANGED,
-	LAST_SIGNAL
-};
 
 static guint monkey_media_audio_cd_signals[LAST_SIGNAL] = { 0 };
 
@@ -152,58 +122,12 @@
 			      G_TYPE_BOOLEAN);
 }
 
-
-static gboolean
-is_cdrom_device (MonkeyMediaAudioCD *cdrom)
-{
-	int fd;
-
-	fd = open (monkey_media_get_cd_drive (), O_RDONLY | O_NONBLOCK);
-	if (fd < 0) 
-	{
-		return FALSE;
-	}
-
-	/* Fire a harmless ioctl at the device. */
-	if (ioctl (fd, CDROM_GET_CAPABILITY, 0) < 0)
-	{
-		/* Failed, it's not a CDROM drive */
-		close (fd);
-		
-		return FALSE;
-	}
-	
-	close (fd);
-
-	return TRUE;
-}
-
 gboolean
 monkey_media_audio_cd_device_available ()
 {
-	int fd;
-
-	fd = open (monkey_media_get_cd_drive (), O_RDONLY | O_NONBLOCK);
-	if (fd < 0) 
-	{
-		return FALSE;
-	}
-
-	/* Fire a harmless ioctl at the device. */
-	if (ioctl (fd, CDROM_GET_CAPABILITY, 0) < 0)
-	{
-		/* Failed, it's not a CDROM drive */
-		close (fd);
-		
-		return FALSE;
-	}
-	
-	close (fd);
-
-	return TRUE;
+	return monkey_media_audio_cd_device_available_impl ();
 }
 
-
 static void
 monkey_media_audio_cd_init (MonkeyMediaAudioCD *cd)
 {
@@ -211,19 +135,20 @@
 
 	cd->priv->lock = g_mutex_new ();
 
-	if (!is_cdrom_device (cd))
+	if (!monkey_media_audio_cd_is_cdrom_device_impl (cd))
 	{
 		cd->priv->error = g_error_new (MONKEY_MEDIA_AUDIO_CD_ERROR,
 					       MONKEY_MEDIA_AUDIO_CD_ERROR_NOT_OPENED,
-					       _("%s does not point to a valid CDRom device. This may be caused by:\n"
-					         "a) CD support is not compiled into Linux\n"
+					       _("%s does not point to a valid CD-ROM device. This may be caused by:\n"
+					         "a) CD support is not compiled into your OS\n"
 					         "b) You do not have the correct permissions to access the CD drive\n"
 					         "c) %s is not the CD drive.\n"),
-					       monkey_media_get_cd_drive (), monkey_media_get_cd_drive ());
+					       monkey_media_get_cd_drive (),
+					       monkey_media_get_cd_drive ());
 		return;
 	}
 
-        cd->priv->poll_func_id = g_timeout_add (1000, (GSourceFunc) poll_event_cb, cd);
+        cd->priv->poll_func_id = g_timeout_add (2000, (GSourceFunc) poll_event_cb, cd);
 
 	cd->priv->valid_info = FALSE;
 	cd->priv->cd_available = monkey_media_audio_cd_available (cd, NULL);
@@ -238,7 +163,8 @@
 
 	g_mutex_free (cd->priv->lock);
 
-	g_source_remove (cd->priv->poll_func_id);
+	if (cd->priv->poll_func_id)
+		g_source_remove (cd->priv->poll_func_id);
 
 	g_free (cd->priv->cd_id);
 	g_free (cd->priv->track_lengths);
@@ -317,53 +243,24 @@
 
 		g_object_unref (G_OBJECT (cd));
 		cd = NULL;
+	} else {
+		global_cd = cd;
+        	g_object_ref (G_OBJECT (cd));
 	}
 
-	global_cd = cd;
-
 	g_mutex_unlock (global_cd_lock);
 
-	return g_object_ref (G_OBJECT (cd));
+	return cd;
 }
 
 static gboolean
 monkey_media_audio_cd_open (MonkeyMediaAudioCD *cd,
 			    GError **error)
 {
-	if (cd->priv->open_count++ == 0) 
-	{
-		cd->priv->fd = open (monkey_media_get_cd_drive (), O_RDONLY | O_NONBLOCK);
-		
-		if (cd->priv->fd < 0) 
-		{
-			if (errno == EACCES && error != NULL)
-			{
-				*error = g_error_new (MONKEY_MEDIA_AUDIO_CD_ERROR,
-					              MONKEY_MEDIA_AUDIO_CD_ERROR_NOT_OPENED,
-					              _("You do not seem to have permission to access %s."),
-					              monkey_media_get_cd_drive ());
-			} 
-			else if (error != NULL) 
-			{
-				*error = g_error_new (MONKEY_MEDIA_AUDIO_CD_ERROR,
-					              MONKEY_MEDIA_AUDIO_CD_ERROR_NOT_OPENED,
-					              _("%s does not appear to point to a valid CD device. This may be because:\n"
-					                "a) CD support is not compiled into Linux\n"
-					                "b) You do not have the correct permissions to access the CD drive\n"
-					                "c) %s is not the CD drive.\n"),
-					              monkey_media_get_cd_drive (),
-					              monkey_media_get_cd_drive ());
-			}
-				
-			cd->priv->open_count = 0;
-			return FALSE;
-		}
-	}
-	
-	return (cd->priv->fd >= 0);
+	return monkey_media_audio_cd_open_impl (cd, error);
 }
 
-static void
+void
 monkey_media_audio_cd_close (MonkeyMediaAudioCD *cd, 
 			     gboolean force_close)
 {
@@ -371,21 +268,13 @@
 	{
 		cd->priv->open_count = 0;
 	}
-
-	if (cd->priv->open_count == 0) 
-	{
-		if (cd->priv->fd >= 0)
-			close (cd->priv->fd);
-		cd->priv->fd = -1;
-	}
+	monkey_media_audio_cd_close_impl (cd);
 }
 
 void
 monkey_media_audio_cd_open_tray (MonkeyMediaAudioCD *cd,
 			         GError **error)
 {
-	int cd_status;
-
 	g_return_if_fail (MONKEY_MEDIA_IS_AUDIO_CD (cd));
 
 	g_mutex_lock (cd->priv->lock);
@@ -396,29 +285,13 @@
 		return;
 	}
 
-	cd_status = ioctl (cd->priv->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
-
-	if (cd_status != -1) 
+	if (monkey_media_audio_cd_open_tray_impl (cd, error))
 	{
-		if (cd_status != CDS_TRAY_OPEN)
-		{
-			if (ioctl (cd->priv->fd, CDROMEJECT, 0) < 0) 
-			{
-				if (error != NULL)
-				{
-					*error = g_error_new (MONKEY_MEDIA_AUDIO_CD_ERROR,
-							      MONKEY_MEDIA_AUDIO_CD_ERROR_SYSTEM_ERROR,
-							      "(monkey_media_audio_cd_open_tray): ioctl failed: %s",
-							      g_strerror (errno));
-				}
-				
-				monkey_media_audio_cd_close (cd, FALSE);
-
-				g_mutex_unlock (cd->priv->lock);
-
-				return;
-			}
-		} 
+		monkey_media_audio_cd_close (cd, FALSE);
+
+		g_mutex_unlock (cd->priv->lock);
+
+		return;
 	}
 
 	monkey_media_audio_cd_close (cd, TRUE);
@@ -440,16 +313,8 @@
 		return;
 	}
 
-	if (ioctl (cd->priv->fd, CDROMCLOSETRAY) < 0) 
+	if (monkey_media_audio_cd_close_tray_impl (cd, error))
 	{
-		if (error != NULL) 
-		{
-			*error = g_error_new (MONKEY_MEDIA_AUDIO_CD_ERROR,
-					      MONKEY_MEDIA_AUDIO_CD_ERROR_SYSTEM_ERROR,
-					      "(monkey_media_audio_cd_close_tray): ioctl failed %s",
-					      g_strerror (errno));
-		}
-
 		monkey_media_audio_cd_close (cd, FALSE);
 		
 		g_mutex_unlock (cd->priv->lock);
@@ -466,7 +331,6 @@
 monkey_media_audio_cd_available (MonkeyMediaAudioCD *cd,
 				 GError **error)
 {
-	int cd_status;
 	gboolean ret;
 
 	g_return_val_if_fail (MONKEY_MEDIA_IS_AUDIO_CD (cd), FALSE);
@@ -478,114 +342,20 @@
 		g_mutex_unlock (cd->priv->lock);
 		return FALSE;
 	}
-
-	cd_status = ioctl (cd->priv->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+	
+	ret = monkey_media_audio_cd_available_impl (cd,error);
 
 	monkey_media_audio_cd_close (cd, TRUE);
 
-	ret = (cd_status == CDS_DISC_OK);
-
 	g_mutex_unlock (cd->priv->lock);
 
 	return ret;
 }
 
-/*
- * Program:	RFC-822 routines (originally from SMTP)
- *
- * Author:	Mark Crispin
- *		Networks and Distributed Computing
- *		Computing & Communications
- *		University of Washington
- *		Administration Building, AG-44
- *		Seattle, WA  98195
- *		Internet: MRC@CAC.Washington.EDU
- *
- * Date:	27 July 1988
- * Last Edited:	10 September 1998
- *
- * Sponsorship:	The original version of this work was developed in the
- *		Symbolic Systems Resources Group of the Knowledge Systems
- *		Laboratory at Stanford University in 1987-88, and was funded
- *		by the Biomedical Research Technology Program of the National
- *		Institutes of Health under grant number RR-00785.
- *
- * Original version Copyright 1988 by The Leland Stanford Junior University
- * Copyright 1998 by the University of Washington
- *
- *  Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee is hereby granted, provided
- * that the above copyright notices appear in all copies and that both the
- * above copyright notices and this permission notice appear in supporting
- * documentation, and that the name of the University of Washington or The
- * Leland Stanford Junior University not be used in advertising or publicity
- * pertaining to distribution of the software without specific, written prior
- * permission.  This software is made available "as is", and
- * THE UNIVERSITY OF WASHINGTON AND THE LELAND STANFORD JUNIOR UNIVERSITY
- * DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE,
- * INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF
- * WASHINGTON OR THE LELAND STANFORD JUNIOR UNIVERSITY BE LIABLE FOR ANY
- * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
- * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
- * CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-/* NOTE: This is not true RFC822 anymore. The use of the characters
-   '/', '+', and '=' is no bueno when the ID will be used as part of a URL.
-   '_', '.', and '-' have been used instead
-*/
-
-/* Convert binary contents to BASE64
- * Accepts: source
- *	    length of source
- *	    pointer to return destination length
- * Returns: destination as BASE64
- */
-
-unsigned char *rfc822_binary (void *src,unsigned long srcl,unsigned long *len)
-{
-  unsigned char *ret,*d;
-  unsigned char *s = (unsigned char *) src;
-  char *v = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
-  unsigned long i = ((srcl + 2) / 3) * 4;
-  *len = i += 2 * ((i / 60) + 1);
-  d = ret = (unsigned char *) malloc ((size_t) ++i);
-  for (i = 0; srcl; s += 3) {	/* process tuplets */
-    *d++ = v[s[0] >> 2];	/* byte 1: high 6 bits (1) */
-				/* byte 2: low 2 bits (1), high 4 bits (2) */
-    *d++ = v[((s[0] << 4) + (--srcl ? (s[1] >> 4) : 0)) & 0x3f];
-				/* byte 3: low 4 bits (2), high 2 bits (3) */
-    *d++ = srcl ? v[((s[1] << 2) + (--srcl ? (s[2] >> 6) : 0)) & 0x3f] : '-';
-				/* byte 4: low 6 bits (3) */
-    *d++ = srcl ? v[s[2] & 0x3f] : '-';
-    if (srcl) srcl--;		/* count third character if processed */
-    if ((++i) == 15) {		/* output 60 characters? */
-      i = 0;			/* restart line break count, insert CRLF */
-      *d++ = '\015'; *d++ = '\012';
-    }
-  }
-  *d = '\0';			/* tie off string */
-
-  return ret;			/* return the resulting string */
-}
-
 static gboolean
 ensure_sync (MonkeyMediaAudioCD *cd,
 	     GError **error)
 {
-	int i, j, *track_frames;
-	struct cdrom_tochdr tochdr;
-	unsigned char track0, track1;
-	struct cdrom_tocentry tocentry;
-	long *frame_offsets;
-	SHA_INFO sha;
-	char *tmp;
-	unsigned char digest[20], *base64;
-	unsigned long size;
- 
         /* Don't recalculate data, valid_data can be changed from
          * callback functions */
         if (cd->priv->valid_info == TRUE)
@@ -594,156 +364,13 @@
 	if (monkey_media_audio_cd_open (cd, error) == FALSE) 
 		return FALSE;
 	
-	if (ioctl (cd->priv->fd, CDROMREADTOCHDR, &tochdr) < 0) 
-	{
-		if (error != NULL)
-		{
-			*error = g_error_new (MONKEY_MEDIA_AUDIO_CD_ERROR,
-					      MONKEY_MEDIA_AUDIO_CD_ERROR_SYSTEM_ERROR,
-					      _("Error reading CD header: %s"),
-					      g_strerror (errno));
-		}
-		monkey_media_audio_cd_close (cd, FALSE);
-		return FALSE;
-	}
-	
-	track0 = tochdr.cdth_trk0;
-	track1 = tochdr.cdth_trk1;
-	cd->priv->n_audio_tracks = track1 - track0 + 1;
-
-        if (cd->priv->track_offsets != NULL)
-        {
-                g_free (cd->priv->track_offsets);
-                g_free (cd->priv->track_lengths);
-        }
-
-	cd->priv->track_offsets = g_new0 (int, cd->priv->n_audio_tracks + 1);
-        cd->priv->track_lengths = g_new0 (int, cd->priv->n_audio_tracks + 1);
-
-	frame_offsets = g_new0 (long, 100);
-	track_frames = g_new0 (int, cd->priv->n_audio_tracks + 1);
-
-	for (i = 0, j = track0; i < cd->priv->n_audio_tracks; i++, j++) 
-	{
-		/* handle time-based stuff */
-		tocentry.cdte_track = j;
-		tocentry.cdte_format = CDROM_MSF;
-
-		if (ioctl (cd->priv->fd, CDROMREADTOCENTRY, &tocentry) < 0) 
-		{
-			g_warning ("IOCtl failed");
-			continue;
-		}
-
-		cd->priv->track_offsets[i] = (tocentry.cdte_addr.msf.minute * 60) 
-                        + tocentry.cdte_addr.msf.second;
-		track_frames[i] = tocentry.cdte_addr.msf.frame;
-
-		/* get frame offest */
-		tocentry.cdte_track = j;
-		tocentry.cdte_format = CDROM_LBA;
-
-		if (ioctl (cd->priv->fd, CDROMREADTOCENTRY, &tocentry) < 0) 
-		{
-			g_warning ("IOCtl failed");
-			continue;
-		}
-		
-		frame_offsets[i + 1] = tocentry.cdte_addr.lba + 150;
-	}
-
-	/* handle time based stuff */
-	tocentry.cdte_track = CDROM_LEADOUT;
-	tocentry.cdte_format = CDROM_MSF;
-	
-	if (ioctl (cd->priv->fd, CDROMREADTOCENTRY, &tocentry) < 0) 
-		goto leadout_error;
-	
-	cd->priv->track_offsets[cd->priv->n_audio_tracks] = (tocentry.cdte_addr.msf.minute * 60) 
-                + tocentry.cdte_addr.msf.second;
-	track_frames[cd->priv->n_audio_tracks] = tocentry.cdte_addr.msf.frame;
-
-	/* get frame offset */
-	tocentry.cdte_track = CDROM_LEADOUT;
-	tocentry.cdte_format = CDROM_LBA;
-
-	if (ioctl (cd->priv->fd, CDROMREADTOCENTRY, &tocentry) < 0)
-		goto leadout_error;
-	
-	frame_offsets[0] = tocentry.cdte_addr.lba + 150;
-
-        for (i = 0; i < cd->priv->n_audio_tracks; i++) 
-        {
-                int f1, f2, df;
-                
-                /* Convert all addresses to frames */
-                f1 = cd->priv->track_offsets[i] * CD_FRAMES + track_frames[i];
-                f2 = cd->priv->track_offsets[i + 1] * CD_FRAMES + track_frames[i + 1];
-                
-                df = f2 - f1;
-                cd->priv->track_lengths[i] = df / CD_FRAMES;
-        }
-
-	/* calculates the musicbrainz disc ID. We do it locally instead of calling
-	 * the lib for this, since we avoid the cd to be accessed yet another time
-	 * this way */
-	sha_init (&sha);
-
-	tmp = g_strdup_printf ("%02X", track0);
-	sha_update (&sha, (unsigned char *) tmp, strlen (tmp));
-	g_free (tmp);
-
-	tmp = g_strdup_printf ("%02X", track1);
-	sha_update (&sha, (unsigned char *) tmp, strlen (tmp));
-	g_free (tmp);
-	
-	for (i = 0; i < 100; i++)
-	{
-		tmp = g_strdup_printf ("%08lX", frame_offsets[i]);
-		sha_update (&sha, (unsigned char *) tmp, strlen (tmp));
-		g_free (tmp);
-	}
-
-	sha_final (digest, &sha);
-
-	base64 = rfc822_binary (digest, 20, &size);
-	if (cd->priv->cd_id != NULL)
-		g_free (cd->priv->cd_id);
-	cd->priv->cd_id = g_strndup (base64, size);
-	g_free (base64);
-
-	g_free (track_frames);
-	g_free (frame_offsets);
-
-	monkey_media_audio_cd_close (cd, TRUE);
-
-        cd->priv->valid_info = TRUE;
-
-	return TRUE;
-
-leadout_error:
-	if (error != NULL)
-	{
-		*error = g_error_new (MONKEY_MEDIA_AUDIO_CD_ERROR,
-				      MONKEY_MEDIA_AUDIO_CD_ERROR_SYSTEM_ERROR,
-				      _("Error getting leadout: %s"),
-				      g_strerror (errno));
-	}
-
-	monkey_media_audio_cd_close (cd, FALSE);
-
-	g_free (track_frames);
-	g_free (frame_offsets);
-
-	return FALSE;
+	return monkey_media_audio_cd_ensure_sync_impl (cd, error);
 }
 
-
 static gboolean
 poll_event_cb (MonkeyMediaAudioCD *cd)
 {
-	int cd_status;
-	gboolean emit_signal = FALSE, available = FALSE;
+	gboolean emit_signal;
 
 	g_mutex_lock (cd->priv->lock);
 
@@ -756,42 +383,8 @@
 
 		return TRUE;
 	}
-	
-	cd_status = ioctl (cd->priv->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
-	if (cd_status != -1) 
-	{
-		switch (cd_status) 
-		{
-		case CDS_NO_INFO:
-		case CDS_NO_DISC:
-		case CDS_TRAY_OPEN:
-		case CDS_DRIVE_NOT_READY:
-			if (cd->priv->cd_available == TRUE)
-			{
-				cd->priv->cd_available = FALSE;
-				cd->priv->valid_info = FALSE;
-
-				emit_signal = TRUE;
-				available = FALSE;
-			}
-                        break;
-		default:
-			if (cd->priv->cd_available == FALSE)
-			{
-				cd->priv->cd_available = TRUE;
-				cd->priv->valid_info = FALSE;
-
-				emit_signal = TRUE;
-				available = TRUE;
-                        }
-			break;
-		}
-	} 
-	else
-	{
-		/* so we went out of sync.. */
-		cd->priv->valid_info = FALSE;
-	}
+
+	emit_signal = monkey_media_audio_cd_poll_event_impl (cd);
 
 	monkey_media_audio_cd_close (cd, FALSE);
 
@@ -799,7 +392,8 @@
 
 	if (emit_signal == TRUE)
 	{
-		g_signal_emit (G_OBJECT (cd), monkey_media_audio_cd_signals[CD_CHANGED], 0, available);
+		monkey_media_debug ("Emitting CD_CHANGED");
+		g_signal_emit (G_OBJECT (cd), monkey_media_audio_cd_signals[CD_CHANGED], 0, cd->priv->cd_available);
 	}
 
 	return TRUE;
@@ -980,3 +574,121 @@
 
 	return quark;
 }
+
+/*
+ * Program:	RFC-822 routines (originally from SMTP)
+ *
+ * Author:	Mark Crispin
+ *		Networks and Distributed Computing
+ *		Computing & Communications
+ *		University of Washington
+ *		Administration Building, AG-44
+ *		Seattle, WA  98195
+ *		Internet: MRC@CAC.Washington.EDU
+ *
+ * Date:	27 July 1988
+ * Last Edited:	10 September 1998
+ *
+ * Sponsorship:	The original version of this work was developed in the
+ *		Symbolic Systems Resources Group of the Knowledge Systems
+ *		Laboratory at Stanford University in 1987-88, and was funded
+ *		by the Biomedical Research Technology Program of the National
+ *		Institutes of Health under grant number RR-00785.
+ *
+ * Original version Copyright 1988 by The Leland Stanford Junior University
+ * Copyright 1998 by the University of Washington
+ *
+ *  Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notices appear in all copies and that both the
+ * above copyright notices and this permission notice appear in supporting
+ * documentation, and that the name of the University of Washington or The
+ * Leland Stanford Junior University not be used in advertising or publicity
+ * pertaining to distribution of the software without specific, written prior
+ * permission.  This software is made available "as is", and
+ * THE UNIVERSITY OF WASHINGTON AND THE LELAND STANFORD JUNIOR UNIVERSITY
+ * DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF
+ * WASHINGTON OR THE LELAND STANFORD JUNIOR UNIVERSITY BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/* NOTE: This is not true RFC822 anymore. The use of the characters
+   '/', '+', and '=' is no bueno when the ID will be used as part of a URL.
+   '_', '.', and '-' have been used instead
+*/
+
+/* Convert binary contents to BASE64
+ * Accepts: source
+ *	    length of source
+ *	    pointer to return destination length
+ * Returns: destination as BASE64
+ */
+
+unsigned char *rfc822_binary (void *src,unsigned long srcl,unsigned long *len)
+{
+  unsigned char *ret,*d;
+  unsigned char *s = (unsigned char *) src;
+  char *v = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
+  unsigned long i = ((srcl + 2) / 3) * 4;
+  *len = i += 2 * ((i / 60) + 1);
+  d = ret = (unsigned char *) malloc ((size_t) ++i);
+  for (i = 0; srcl; s += 3) {	/* process tuplets */
+    *d++ = v[s[0] >> 2];	/* byte 1: high 6 bits (1) */
+				/* byte 2: low 2 bits (1), high 4 bits (2) */
+    *d++ = v[((s[0] << 4) + (--srcl ? (s[1] >> 4) : 0)) & 0x3f];
+				/* byte 3: low 4 bits (2), high 2 bits (3) */
+    *d++ = srcl ? v[((s[1] << 2) + (--srcl ? (s[2] >> 6) : 0)) & 0x3f] : '-';
+				/* byte 4: low 6 bits (3) */
+    *d++ = srcl ? v[s[2] & 0x3f] : '-';
+    if (srcl) srcl--;		/* count third character if processed */
+    if ((++i) == 15) {		/* output 60 characters? */
+      i = 0;			/* restart line break count, insert CRLF */
+      *d++ = '\015'; *d++ = '\012';
+    }
+  }
+  *d = '\0';			/* tie off string */
+
+  return ret;			/* return the resulting string */
+}
+
+char *
+monkey_media_audio_cd_calculate_musicbrainz_id (unsigned char track0,
+						unsigned char track1,
+						long *frame_offsets)
+{
+	SHA_INFO sha;
+	unsigned long size;
+	char *tmp;
+	int i;
+	unsigned char digest[20], *base64;
+	/* calculates the musicbrainz disc ID. We do it locally instead of calling
+	 * the lib for this, since we avoid the cd to be accessed yet another time
+	 * this way */
+	sha_init (&sha);
+
+	tmp = g_strdup_printf ("%02X", track0);
+	sha_update (&sha, (unsigned char *) tmp, strlen (tmp));
+	g_free (tmp);
+
+	tmp = g_strdup_printf ("%02X", track1);
+	sha_update (&sha, (unsigned char *) tmp, strlen (tmp));
+	g_free (tmp);
+	
+	for (i = 0; i < 100; i++)
+	{
+		tmp = g_strdup_printf ("%08lX", frame_offsets[i]);
+		sha_update (&sha, (unsigned char *) tmp, strlen (tmp));
+		g_free (tmp);
+	}
+
+	sha_final (digest, &sha);
+
+	base64 = rfc822_binary (digest, 20, &size);
+	return g_strndup (base64, size);
+}
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/monkey-media-audio-cd.h net-rhythmbox/monkey-media/monkey-media-audio-cd.h
--- monkey-media/src/monkey-media-audio-cd.h	2003-01-12 14:46:09.000000000 -0500
+++ net-rhythmbox/monkey-media/monkey-media-audio-cd.h	2003-04-25 01:51:47.000000000 -0400
@@ -74,6 +74,9 @@
 gboolean            monkey_media_audio_cd_available   (MonkeyMediaAudioCD *cd,
 						       GError **error);
 
+gboolean	    monkey_media_audio_cd_have_track  (MonkeyMediaAudioCD *cd,
+						       int track,
+						       GError **error);
 /* returns a list of uri */
 GList              *monkey_media_audio_cd_list_tracks (MonkeyMediaAudioCD *cd,
 						       GError **error);
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/monkey-media-audio-cd-linux.c net-rhythmbox/monkey-media/monkey-media-audio-cd-linux.c
--- monkey-media/src/monkey-media-audio-cd-linux.c	1969-12-31 19:00:00.000000000 -0500
+++ net-rhythmbox/monkey-media/monkey-media-audio-cd-linux.c	2003-04-25 16:43:55.000000000 -0400
@@ -0,0 +1,377 @@
+/*  monkey-media
+ *  Copyright (C) 2001 Iain Holmes <iain@ximian.com>
+ *                2002 Kenneth Christiansen <kenneth@gnu.org>
+ *                     Olivier Martin <omartin@ifrance.com>
+ *                     Jorn Baayen <jorn@nl.linux.org>
+ *                2003 Colin Walters <walters@verbum.org>
+ *  
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  $Id: monkey-media-audio-cd-linux.c,v 1.2 2003/04/25 20:43:55 cwalters Exp $
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/cdrom.h>
+
+#include "monkey-media.h"
+#include "monkey-media-private.h"
+#include "monkey-media-audio-cd.h"
+#include "monkey-media-audio-cd-private.h"
+
+gboolean
+monkey_media_audio_cd_is_cdrom_device_impl (MonkeyMediaAudioCD *cdrom)
+{
+	int fd;
+
+	fd = open (monkey_media_get_cd_drive (), O_RDONLY | O_NONBLOCK);
+	if (fd < 0) 
+	{
+		return FALSE;
+	}
+
+	/* Fire a harmless ioctl at the device. */
+	if (ioctl (fd, CDROM_GET_CAPABILITY, 0) < 0)
+	{
+		/* Failed, it's not a CDROM drive */
+		close (fd);
+		
+		return FALSE;
+	}
+	
+	close (fd);
+
+	return TRUE;
+}
+
+gboolean
+monkey_media_audio_cd_device_available_impl ()
+{
+	int fd;
+	const char *drive = monkey_media_get_cd_drive ();
+
+	fd = open (drive, O_RDONLY | O_NONBLOCK);
+	if (fd < 0) 
+	{
+		monkey_media_debug ("Failed to open %s: %s", drive,
+				    strerror (errno));
+		return FALSE;
+	}
+
+	/* Fire a harmless ioctl at the device. */
+	if (ioctl (fd, CDROM_GET_CAPABILITY, 0) < 0)
+	{
+		/* Failed, it's not a CDROM drive */
+		close (fd);
+		
+		monkey_media_debug ("CDROM_GET_CAPABILITY failed for %s", drive);
+		return FALSE;
+	}
+	
+	close (fd);
+
+	return TRUE;
+}
+
+gboolean
+monkey_media_audio_cd_open_impl (MonkeyMediaAudioCD *cd,
+				 GError **error)
+{
+	if (cd->priv->open_count++ == 0)
+	{
+		cd->priv->fd = open (monkey_media_get_cd_drive (), O_RDONLY | O_NONBLOCK);
+		
+		if (cd->priv->fd < 0) 
+		{
+			if (errno == EACCES && error != NULL)
+			{
+				*error = g_error_new (MONKEY_MEDIA_AUDIO_CD_ERROR,
+						      MONKEY_MEDIA_AUDIO_CD_ERROR_NOT_OPENED,
+						      _("You do not seem to have permission to access %s."),
+						      monkey_media_get_cd_drive ());
+			} 
+			else if (error != NULL) 
+			{
+				*error = g_error_new (MONKEY_MEDIA_AUDIO_CD_ERROR,
+						      MONKEY_MEDIA_AUDIO_CD_ERROR_NOT_OPENED,
+						      _("%s does not appear to point to a valid CD device. This may be because:\n"
+							"a) CD support is not compiled into Linux\n"
+							"b) You do not have the correct permissions to access the CD drive\n"
+							"c) %s is not the CD drive.\n"),
+						      monkey_media_get_cd_drive (),
+						      monkey_media_get_cd_drive ());
+			}
+				
+			cd->priv->open_count = 0;
+			return FALSE;
+		}
+	
+	}
+	return (cd->priv->fd >= 0);
+}
+
+void
+monkey_media_audio_cd_close_impl (MonkeyMediaAudioCD *cd)
+{
+	if (cd->priv->open_count == 0) 
+	{
+		if (cd->priv->fd >= 0)
+			close (cd->priv->fd);
+		cd->priv->fd = -1;
+	}
+}
+
+gboolean
+monkey_media_audio_cd_open_tray_impl (MonkeyMediaAudioCD *cd,
+				      GError **error)
+{
+	int cd_status;
+
+	cd_status = ioctl (cd->priv->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+
+	if (cd_status != -1 && cd_status != CDS_TRAY_OPEN
+	    && ioctl (cd->priv->fd, CDROMEJECT, 0) < 0)
+	{
+		if (error != NULL)
+		{
+			*error = g_error_new (MONKEY_MEDIA_AUDIO_CD_ERROR,
+					      MONKEY_MEDIA_AUDIO_CD_ERROR_SYSTEM_ERROR,
+					      "(monkey_media_audio_cd_open_tray): ioctl failed: %s",
+					      g_strerror (errno));
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+gboolean
+monkey_media_audio_cd_close_tray_impl (MonkeyMediaAudioCD *cd,
+				       GError **error)
+{
+	if (ioctl (cd->priv->fd, CDROMCLOSETRAY) < 0) 
+	{
+		if (error != NULL) 
+		{
+			*error = g_error_new (MONKEY_MEDIA_AUDIO_CD_ERROR,
+					      MONKEY_MEDIA_AUDIO_CD_ERROR_SYSTEM_ERROR,
+					      "(monkey_media_audio_cd_close_tray): ioctl failed %s",
+					      g_strerror (errno));
+			return TRUE;
+		}
+	}
+	return FALSE;	
+}
+
+gboolean
+monkey_media_audio_cd_available_impl (MonkeyMediaAudioCD *cd,
+				      GError **error)
+{
+	int cd_status = ioctl (cd->priv->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+
+	return (cd_status == CDS_DISC_OK);
+}
+
+gboolean
+monkey_media_audio_cd_ensure_sync_impl (MonkeyMediaAudioCD *cd,
+					GError **error)
+{
+	int i, j, *track_frames;
+	struct cdrom_tochdr tochdr;
+	unsigned char track0, track1;
+	struct cdrom_tocentry tocentry;
+	long *frame_offsets;
+ 
+	monkey_media_debug ("Trying CDROMREADTOCHDR");
+	if (ioctl (cd->priv->fd, CDROMREADTOCHDR, &tochdr) < 0) 
+	{
+		if (error != NULL)
+		{
+			*error = g_error_new (MONKEY_MEDIA_AUDIO_CD_ERROR,
+					      MONKEY_MEDIA_AUDIO_CD_ERROR_SYSTEM_ERROR,
+					      _("Error reading CD header: %s"),
+					      g_strerror (errno));
+		}
+		monkey_media_audio_cd_close (cd, FALSE);
+		monkey_media_debug ("CDROMREADTOCHDR failed!");
+		return FALSE;
+	}
+	
+	track0 = tochdr.cdth_trk0;
+	track1 = tochdr.cdth_trk1;
+	cd->priv->n_audio_tracks = track1 - track0 + 1;
+	monkey_media_debug ("Got %d tracks", cd->priv->n_audio_tracks);
+
+        if (cd->priv->track_offsets != NULL)
+        {
+                g_free (cd->priv->track_offsets);
+                g_free (cd->priv->track_lengths);
+        }
+
+	cd->priv->track_offsets = g_new0 (int, cd->priv->n_audio_tracks + 1);
+        cd->priv->track_lengths = g_new0 (int, cd->priv->n_audio_tracks + 1);
+
+	frame_offsets = g_new0 (long, 100);
+	track_frames = g_new0 (int, cd->priv->n_audio_tracks + 1);
+
+	for (i = 0, j = track0; i < cd->priv->n_audio_tracks; i++, j++) 
+	{
+		/* handle time-based stuff */
+		tocentry.cdte_track = j;
+		tocentry.cdte_format = CDROM_MSF;
+
+		monkey_media_debug ("Trying CDROMREADTOCENTRY: %d, CDROM_MSF", j);
+		if (ioctl (cd->priv->fd, CDROMREADTOCENTRY, &tocentry) < 0) 
+		{
+			g_warning ("IOCtl failed");
+			continue;
+		}
+
+		cd->priv->track_offsets[i] = (tocentry.cdte_addr.msf.minute * 60) 
+                        + tocentry.cdte_addr.msf.second;
+		track_frames[i] = tocentry.cdte_addr.msf.frame;
+
+		/* get frame offest */
+		tocentry.cdte_track = j;
+		tocentry.cdte_format = CDROM_LBA;
+
+		monkey_media_debug ("Trying CDROMREADTOCENTRY: %d, CDROM_LBA", j);
+		if (ioctl (cd->priv->fd, CDROMREADTOCENTRY, &tocentry) < 0) 
+		{
+			g_warning ("IOCtl failed");
+			continue;
+		}
+		
+		frame_offsets[i + 1] = tocentry.cdte_addr.lba + 150;
+	}
+
+	/* handle time based stuff */
+	tocentry.cdte_track = CDROM_LEADOUT;
+	tocentry.cdte_format = CDROM_MSF;
+	
+	monkey_media_debug ("Trying CDROMREADTOCENTRY with CDROM_LEADOUT, CDROM_MSF");
+	if (ioctl (cd->priv->fd, CDROMREADTOCENTRY, &tocentry) < 0) 
+		goto leadout_error;
+	
+	cd->priv->track_offsets[cd->priv->n_audio_tracks] = (tocentry.cdte_addr.msf.minute * 60) 
+                + tocentry.cdte_addr.msf.second;
+	track_frames[cd->priv->n_audio_tracks] = tocentry.cdte_addr.msf.frame;
+
+	/* get frame offset */
+	tocentry.cdte_track = CDROM_LEADOUT;
+	tocentry.cdte_format = CDROM_LBA;
+
+	monkey_media_debug ("Trying CDROMREADTOCENTRY with CDROM_LEADOUT, CDROM_LBA");
+	if (ioctl (cd->priv->fd, CDROMREADTOCENTRY, &tocentry) < 0)
+		goto leadout_error;
+	
+	frame_offsets[0] = tocentry.cdte_addr.lba + 150;
+
+        for (i = 0; i < cd->priv->n_audio_tracks; i++) 
+        {
+                int f1, f2, df;
+                
+                /* Convert all addresses to frames */
+                f1 = cd->priv->track_offsets[i] * CD_FRAMES + track_frames[i];
+                f2 = cd->priv->track_offsets[i + 1] * CD_FRAMES + track_frames[i + 1];
+                
+                df = f2 - f1;
+                cd->priv->track_lengths[i] = df / CD_FRAMES;
+        }
+
+	if (cd->priv->cd_id != NULL)
+		g_free (cd->priv->cd_id);
+	cd->priv->cd_id = monkey_media_audio_cd_calculate_musicbrainz_id (track0,
+									  track1,
+									  frame_offsets);
+	monkey_media_debug ("Calculated MusicBrainz ID \"%s\"",
+			    cd->priv->cd_id);
+
+	g_free (track_frames);
+	g_free (frame_offsets);
+
+	monkey_media_audio_cd_close (cd, TRUE);
+
+        cd->priv->valid_info = TRUE;
+
+	return TRUE;
+
+leadout_error:
+	if (error != NULL)
+	{
+		*error = g_error_new (MONKEY_MEDIA_AUDIO_CD_ERROR,
+				      MONKEY_MEDIA_AUDIO_CD_ERROR_SYSTEM_ERROR,
+				      _("Error getting leadout: %s"),
+				      g_strerror (errno));
+	}
+
+	monkey_media_audio_cd_close (cd, FALSE);
+
+	g_free (track_frames);
+	g_free (frame_offsets);
+
+	return FALSE;
+}
+
+gboolean
+monkey_media_audio_cd_poll_event_impl (MonkeyMediaAudioCD *cd)
+{
+	int cd_status;
+	gboolean emit_signal = FALSE;
+	cd_status = ioctl (cd->priv->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+	if (cd_status != -1) 
+	{
+		switch (cd_status) 
+		{
+		case CDS_NO_INFO:
+		case CDS_NO_DISC:
+		case CDS_TRAY_OPEN:
+		case CDS_DRIVE_NOT_READY:
+			if (cd->priv->cd_available == TRUE)
+			{
+				cd->priv->cd_available = FALSE;
+				cd->priv->valid_info = FALSE;
+
+				monkey_media_debug ("CD not available");
+				emit_signal = TRUE;
+			}
+                        break;
+		default:
+			if (cd->priv->cd_available == FALSE)
+			{
+				monkey_media_debug ("Got CD available!");
+				cd->priv->cd_available = TRUE;
+				cd->priv->valid_info = FALSE;
+
+				emit_signal = TRUE;
+                        }
+			break;
+		}
+	} 
+	else
+	{
+		/* so we went out of sync.. */
+		cd->priv->valid_info = FALSE;
+	}
+	return emit_signal;
+}
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/monkey-media-audio-cd-private.h net-rhythmbox/monkey-media/monkey-media-audio-cd-private.h
--- monkey-media/src/monkey-media-audio-cd-private.h	2002-09-24 14:23:28.000000000 -0400
+++ net-rhythmbox/monkey-media/monkey-media-audio-cd-private.h	2003-04-25 13:59:19.000000000 -0400
@@ -28,6 +28,41 @@
 
 G_BEGIN_DECLS
 
+struct MonkeyMediaAudioCDPrivate 
+{
+	int open_count;
+
+        int poll_func_id;
+
+	GError *error;
+
+	int fd;
+
+        gboolean valid_info;
+
+	gboolean cd_available;
+	
+        int n_audio_tracks;
+        int *track_lengths;
+        int *track_offsets;
+
+	char *cd_id;
+
+	GMutex *lock;
+};
+
+enum
+{
+	PROP_0,
+	PROP_ERROR
+};
+
+enum
+{
+	CD_CHANGED,
+	LAST_SIGNAL
+};
+
 long     monkey_media_audio_cd_get_track_duration (MonkeyMediaAudioCD *cd,
 					           int track,
 					           GError **error);
@@ -42,6 +77,39 @@
 
 void     monkey_media_audio_cd_unref_if_around    (void);
 
+void     monkey_media_audio_cd_close (MonkeyMediaAudioCD *cd, 
+				      gboolean force_close);
+
+char *   monkey_media_audio_cd_calculate_musicbrainz_id (unsigned char track0,
+							 unsigned char track1,
+							 long *frame_offests);
+/**
+ * Subsystem-specific prototypes
+ */
+gboolean monkey_media_audio_cd_is_cdrom_device_impl (MonkeyMediaAudioCD *cdrom);
+
+gboolean monkey_media_audio_cd_device_available_impl ();
+
+gboolean monkey_media_audio_cd_open_impl (MonkeyMediaAudioCD *cd,
+					  GError **error);
+
+void     monkey_media_audio_cd_close_impl (MonkeyMediaAudioCD *cd);
+
+gboolean monkey_media_audio_cd_open_tray_impl (MonkeyMediaAudioCD *cd,
+					       GError **error);
+
+gboolean monkey_media_audio_cd_close_tray_impl (MonkeyMediaAudioCD *cd,
+						GError **error);
+
+gboolean monkey_media_audio_cd_available_impl (MonkeyMediaAudioCD *cd,
+					       GError **error);
+
+gboolean monkey_media_audio_cd_ensure_sync_impl (MonkeyMediaAudioCD *cd,
+						 GError **error);
+
+gboolean monkey_media_audio_cd_poll_event_impl (MonkeyMediaAudioCD *cd);
+
+
 G_END_DECLS
 
 #endif /* __MONKEY_MEDIA_AUDIO_CD_PRIVATE_H */
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/monkey-media.c net-rhythmbox/monkey-media/monkey-media.c
--- monkey-media/src/monkey-media.c	2003-04-19 11:00:33.000000000 -0400
+++ net-rhythmbox/monkey-media/monkey-media.c	2003-04-25 16:36:40.000000000 -0400
@@ -18,7 +18,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
- *  $Id: monkey-media.c,v 1.42 2003/04/19 13:21:54 jbaayen Exp $
+ *  $Id: monkey-media.c,v 1.41 2003/02/01 17:43:33 jbaayen Exp $
  */
 
 #include <popt.h>
@@ -39,6 +39,7 @@
 #endif
 
 #include "monkey-media.h"
+#include "monkey-media-debug.h"
 #include "monkey-media-stream-info.h"
 #include "monkey-media-private.h"
 #include "monkey-media-audio-cd-private.h"
@@ -98,7 +99,7 @@
 }
 
 void
-nonkey_media_init_with_popt_table (int *argc, char ***argv,
+monkey_media_init_with_popt_table (int *argc, char ***argv,
 	                           const struct poptOption *popt_options)
 {
 	if (monkey_media_is_alive () == TRUE) return;
@@ -179,10 +180,10 @@
                        TYPE_FLAC_STREAM_INFO_IMPL);
 	register_type ("audio/x-mp3",
 		       TYPE_MP3_STREAM_INFO_IMPL);
-	register_type ("audio/mpeg",
-		       TYPE_MP3_STREAM_INFO_IMPL);
+#ifdef HAVE_AUDIOCD
 	register_type ("audiocd",
 		       TYPE_AUDIOCD_STREAM_INFO_IMPL);
+#endif	
 
 	/* be sure dir exists */
 	mmdir = g_build_filename (g_get_home_dir (),
@@ -230,7 +231,9 @@
 
 	alive = FALSE;
 
+#ifdef HAVE_AUDIOCD
 	monkey_media_audio_cd_unref_if_around ();
+#endif	
 	monkey_media_musicbrainz_unref_if_around ();
 
 	g_free (cd_drive);
@@ -316,7 +319,7 @@
 
 	g_return_val_if_fail (alive == TRUE, NULL);
 
-	/* we default to osssink */
+	/* we default to /dev/cdrom */
 	if (ret == NULL)
 	{
 		g_warning ("Could not find value for " CONF_KEY_CD_DRIVE ", this means that either monkey-media"
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/monkey-media-debug.c net-rhythmbox/monkey-media/monkey-media-debug.c
--- monkey-media/src/monkey-media-debug.c	1969-12-31 19:00:00.000000000 -0500
+++ net-rhythmbox/monkey-media/monkey-media-debug.c	2003-04-17 14:33:27.000000000 -0400
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (C) 2002 Jorn Baayen
+ *  Copyright (C) 2003 Colin Walters <walters@verbum.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  NOTES: log domain hack stolen from nautilus
+ *
+ *  $Id: rb-debug.c,v 1.2 2002/08/18 20:23:36 jbaayen Exp $
+ */
+
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <time.h>
+
+#include "monkey-media-debug.h"
+
+static gboolean debugging = FALSE;
+
+/* Our own funky debugging function, should only be used when something
+ * is not going wrong, if something *is* wrong use g_warning.
+ */
+void
+monkey_media_debug_real (const char *func,
+		  	 const char *file,
+			 const int line,
+			 const char *format, ...)
+{
+	va_list args;
+	char buffer[1025];
+	char *str_time;
+	time_t the_time;
+
+	if (debugging == FALSE) return;
+
+	va_start (args, format);
+
+	vsnprintf (buffer, 1024, format, args);
+	
+	va_end (args);
+
+	time (&the_time);
+	str_time = g_new0 (char, 255);
+	strftime (str_time, 254, "%H:%M:%S", localtime (&the_time));
+
+	g_printerr ("[%s] %s:%d (%s): %s\n", func, file, line, str_time, buffer);
+	
+	g_free (str_time);
+}
+
+void
+monkey_media_debug_init (gboolean debug)
+{
+	debugging = debug;
+
+	monkey_media_debug ("Debugging enabled");
+}
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/monkey-media-debug.h net-rhythmbox/monkey-media/monkey-media-debug.h
--- monkey-media/src/monkey-media-debug.h	1969-12-31 19:00:00.000000000 -0500
+++ net-rhythmbox/monkey-media/monkey-media-debug.h	2003-04-17 14:33:27.000000000 -0400
@@ -0,0 +1,46 @@
+/*
+ *  Copyright (C) 2002 Jorn Baayen
+ *  Copyright (C) 2003 Colin Walters <walters@verbum.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  $Id: rb-debug.h,v 1.2 2002/08/18 20:23:36 jbaayen Exp $
+ */
+
+#ifndef __MM_DEBUG_H
+#define __MM_DEBUG_H
+
+#include <stdarg.h>
+#include <glib.h>
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#define monkey_media_debug(...) monkey_media_debug_real (__func__, __FILE__, __LINE__, __VA_ARGS__)
+#else
+#define monkey_media_debug(...) monkey_media_debug_real ("", __FILE__, __LINE__, __VA_ARGS__)
+#endif
+
+
+G_BEGIN_DECLS
+
+void monkey_media_debug_init             (gboolean debug);
+
+void monkey_media_debug_real             (const char *func,
+					  const char *file,
+					  int line,
+					  const char *format, ...);
+
+G_END_DECLS
+
+#endif /* __MM_DEBUG_H */
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/monkey-media.h net-rhythmbox/monkey-media/monkey-media.h
--- monkey-media/src/monkey-media.h	2003-04-19 11:00:33.000000000 -0400
+++ net-rhythmbox/monkey-media/monkey-media.h	2003-02-09 03:10:17.000000000 -0500
@@ -18,7 +18,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
- *  $Id: monkey-media.h,v 1.16 2003/04/19 14:13:53 jbaayen Exp $
+ *  $Id: monkey-media.h,v 1.15 2003/01/12 19:46:10 jbaayen Exp $
  */
 
 #ifndef __MONKEY_MEDIA_H
@@ -53,8 +53,8 @@
 typedef enum
 {
 	MONKEY_MEDIA_CD_PLAYBACK_NO_ERROR_CORRECTION     = 0,
-	MONKEY_MEDIA_CD_PLAYBACK_MEDIUM_ERROR_CORRECTION = 4,
-	MONKEY_MEDIA_CD_PLAYBACK_FULL_ERROR_CORRECTION   = 255
+	MONKEY_MEDIA_CD_PLAYBACK_MEDIUM_ERROR_CORRECTION = 1,
+	MONKEY_MEDIA_CD_PLAYBACK_FULL_ERROR_CORRECTION   = 2
 } MonkeyMediaCDPlaybackMode;
 
 MonkeyMediaCDPlaybackMode monkey_media_get_cd_playback_mode (void);
@@ -63,6 +63,7 @@
 G_END_DECLS
 
 #include "monkey-media-player.h"
+#include "monkey-media-debug.h"
 #include "monkey-media-stream-info.h"
 #include "monkey-media-audio-cd.h"
 #include "monkey-media-audio-quality.h"
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/monkey-media-musicbrainz.c net-rhythmbox/monkey-media/monkey-media-musicbrainz.c
--- monkey-media/src/monkey-media-musicbrainz.c	2003-04-19 11:00:33.000000000 -0400
+++ net-rhythmbox/monkey-media/monkey-media-musicbrainz.c	2003-04-25 18:17:38.000000000 -0400
@@ -15,7 +15,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
- *  $Id: monkey-media-musicbrainz.c,v 1.9 2003/04/12 11:36:33 jbaayen Exp $
+ *  $Id: monkey-media-musicbrainz.c,v 1.8 2003/01/12 19:46:10 jbaayen Exp $
  */
 
 #include <config.h>
@@ -192,7 +192,7 @@
 	mb->priv->mb_dir = g_build_filename (monkey_media_get_dir (),
 					     "cached_metadata",
 					     NULL);
-	mb->priv->album_dir = g_build_filename (mb->priv->mb_dir,
+	mb->priv->album_dir = g_build_filename (monkey_media_get_dir (),
 						"albums",
 						NULL);
 
@@ -456,6 +456,7 @@
 			      data, 256))
 	{
 		info->title = g_strdup (data);
+		monkey_media_debug ("Got title: %s", data);
 	}
 
 	info->title_id = g_strdup (id);
@@ -465,6 +466,7 @@
 			      data, 256))
 	{
 		info->artist = g_strdup (data);
+		monkey_media_debug ("Got artist: %s", data);
 	}
 
 	if (mb_GetResultData (mb->priv->mb,
@@ -472,6 +474,7 @@
 			      data, 256))
 	{
 		info->artist_id = g_strdup (data);
+		monkey_media_debug ("Got artist ID: %s", data);
 	}
 
 	if (mb_GetResultData (mb->priv->mb,
@@ -479,13 +482,15 @@
 			      data, 256))
 	{
 		info->album = g_strdup (data);
+		monkey_media_debug ("Got album: %s", data);
 	}
 
 	if (mb_GetResultData (mb->priv->mb,
-			      MBE_LookupGetAlbumId,
+			      MBE_AlbumGetAlbumId,
 			      data, 256))
 	{
 		info->album_id = g_strdup (data);
+		monkey_media_debug ("Got album ID: %s", data);
 	}
 	
 	info->track_number = mb_GetResultInt (mb->priv->mb,
@@ -599,6 +604,7 @@
 				       data, 256, i))
 		{
 			info->title = g_strdup (data);
+			monkey_media_debug ("Got title: %s", data);
 		}
 
 		if (mb_GetResultData1 (mb->priv->mb,
@@ -606,6 +612,7 @@
 				       data, 256, i))
 		{
 			info->title_id = g_strdup (data);
+			monkey_media_debug ("Got title ID: %s", data);
 		}
 
 		if (mb_GetResultData1 (mb->priv->mb,
@@ -613,6 +620,7 @@
 				       data, 256, i))
 		{
 			info->artist = g_strdup (data);
+			monkey_media_debug ("Got artist: %s", data);
 		}
 
 		if (mb_GetResultData1 (mb->priv->mb,
@@ -620,6 +628,7 @@
 				       data, 256, i))
 		{
 			info->artist_id = g_strdup (data);
+			monkey_media_debug ("Got artist ID: %s", data);
 		}
 
 		info->album = g_strdup (disc_title);
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/monkey-media-player-gst.c net-rhythmbox/monkey-media/monkey-media-player-gst.c
--- monkey-media/src/monkey-media-player-gst.c	2003-04-19 11:00:33.000000000 -0400
+++ net-rhythmbox/monkey-media/monkey-media-player-gst.c	2003-04-25 16:35:15.000000000 -0400
@@ -1,5 +1,6 @@
 /*  monkey-media
  *  Copyright (C) 2003 Jorn Baayen <jorn@nl.linux.org>
+ *  Copyright (C) 2003 Colin Walters <walters@debian.org>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -15,24 +16,26 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
- *  $Id: monkey-media-player-gst.c,v 1.11 2003/04/19 14:45:50 jbaayen Exp $
+ *  $Id: monkey-media-player-gst.c,v 1.8 2003/02/01 17:43:33 jbaayen Exp $
  */
 
 #include <config.h>
 
 #ifdef HAVE_GSTREAMER
 #include <gst/gst.h>
+#include <gst/gstqueue.h>
 #include <gst/gconf/gconf.h>
 #include <gst/control/control.h>
 #include <gst/control/dparam_smooth.h>
 #include <math.h>
 #include <string.h>
+#include <libgnomevfs/gnome-vfs-ops.h>
 #include <libgnomevfs/gnome-vfs-utils.h>
 
 #include "monkey-media.h"
 #include "monkey-media-marshal.h"
 #include "monkey-media-private.h"
-#include "monkey-media-audio-cd-private.h"
+#include "monkey-media-audio-cd.h"
 
 static void monkey_media_player_class_init (MonkeyMediaPlayerClass *klass);
 static void monkey_media_player_init (MonkeyMediaPlayer *mp);
@@ -43,12 +46,25 @@
 	char *uri;
 
 	GstElement *pipeline;
+
+	GstElement *srcthread;
+	GstElement *queue;
+	GstElement *waiting_bin;
+	gboolean use_buffer;
 	GstElement *src;
 	GstElement *audiocd_src;
+
 	GstElement *decoder;
 	GstElement *volume;
 	GstElement *sink;
 
+	gboolean audiocd_mode;
+#ifdef HAVE_AUDIOCD
+	MonkeyMediaAudioCD *cd;
+#endif	
+
+	gboolean queue_full_blocked;
+
 	gboolean playing;
 
 	guint error_signal_id;
@@ -57,17 +73,17 @@
 	float cur_volume;
 	gboolean mute;
 
-	gboolean audiocd_mode;
+	gboolean have_sane_pipeline;
 
 	guint tick_timeout_id;
-
-	MonkeyMediaAudioCD *cd;
 };
 
 typedef enum
 {
 	EOS,
 	INFO,
+	BUFFERING_BEGIN,
+	BUFFERING_END,
 	ERROR,
 	TICK,
 	LAST_SIGNAL
@@ -141,6 +157,24 @@
 			      2,
 			      MONKEY_MEDIA_TYPE_STREAM_INFO_FIELD,
 			      G_TYPE_POINTER);
+	monkey_media_player_signals[BUFFERING_BEGIN] =
+		g_signal_new ("buffering_begin",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (MonkeyMediaPlayerClass, buffering_begin),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+	monkey_media_player_signals[BUFFERING_END] =
+		g_signal_new ("buffering_end",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (MonkeyMediaPlayerClass, buffering_end),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
 	monkey_media_player_signals[ERROR] =
 		g_signal_new ("error",
 			      G_OBJECT_CLASS_TYPE (object_class),
@@ -180,7 +214,9 @@
 {
 	mp->priv = g_new0 (MonkeyMediaPlayerPrivate, 1);
 
+#ifdef HAVE_AUDIOCD
 	mp->priv->cd = monkey_media_audio_cd_new (NULL);
+#endif
 
 	mp->priv->tick_timeout_id = g_timeout_add (200, (GSourceFunc) tick_timeout, mp);
 }
@@ -200,18 +236,17 @@
 	gst_element_set_state (mp->priv->pipeline,
 			       GST_STATE_NULL);
 
+	gst_object_unref (GST_OBJECT (mp->priv->src));
+	gst_object_unref (GST_OBJECT (mp->priv->audiocd_src));
 	gst_object_unref (GST_OBJECT (mp->priv->pipeline));
 
-	if (mp->priv->audiocd_mode) {
-		gst_object_unref (GST_OBJECT (mp->priv->src));
-		gst_object_unref (GST_OBJECT (mp->priv->decoder));
-	} else {
-		gst_object_unref (GST_OBJECT (mp->priv->audiocd_src));
-	}
-
+#ifdef HAVE_AUDIOCD
 	if (mp->priv->cd != NULL) {
 		g_object_unref (G_OBJECT (mp->priv->cd));
 	}
+#endif	
+
+	mp->priv->have_sane_pipeline = FALSE;
 
 	g_free (mp->priv->uri);
 
@@ -230,6 +265,27 @@
 	return FALSE;
 }
 
+static gboolean
+buffering_begin_signal_idle (MonkeyMediaPlayer *mp)
+{
+	g_signal_emit (G_OBJECT (mp), monkey_media_player_signals[BUFFERING_BEGIN], 0);
+
+	g_object_unref (G_OBJECT (mp));
+
+	return FALSE;
+}
+
+static gboolean
+buffering_end_signal_idle (MonkeyMediaPlayer *mp)
+{
+	g_signal_emit (G_OBJECT (mp), monkey_media_player_signals[BUFFERING_END], 0);
+
+	g_object_unref (G_OBJECT (mp));
+
+	return FALSE;
+}
+
+
 static void
 eos_cb (GstElement *element,
 	MonkeyMediaPlayer *mp)
@@ -290,6 +346,41 @@
 	return FALSE;
 }
 
+#if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 6
+static char *
+unicodify (const char *str, int len, ...)
+{
+	char *ret = NULL, *cset;
+	va_list args;
+	int bytes_read, bytes_written;
+
+	if (g_utf8_validate (str, len, NULL))
+		return g_strndup (str, len >= 0 ? len : strlen (str));
+
+	va_start (args, len);
+	while ((cset = va_arg (args, char *)) != NULL)
+	{
+		if (!strcmp (cset, "locale"))
+			ret = g_locale_to_utf8 (str, len, &bytes_read,
+						&bytes_written, NULL);
+		else
+			ret = g_convert (str, len, "UTF-8", cset,
+					 &bytes_read, &bytes_written, NULL);
+		if (ret)
+			break;
+	}
+	va_end (args);
+
+	return ret;
+}
+
+static char *
+monkey_media_unicodify (const char *str)
+{
+	return unicodify (str, -1, "locale", "ISO-8859-1", NULL);
+}
+#endif
+
 static void
 deep_notify_cb (GstElement *element, GstElement *orig,
 	        GParamSpec *pspec, MonkeyMediaPlayer *player)
@@ -306,6 +397,12 @@
 	g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
 	g_object_get_property (G_OBJECT (orig), pspec->name, value);
 
+	{
+		char *contents = g_strdup_value_contents (value);
+		monkey_media_debug ("got deep_notify: %s -> %s", pspec->name, contents);
+		g_free (contents);
+	}
+
 	/* Other properties from the gnomevfssrc go here */
 	if (strcmp (pspec->name, "iradio-title") == 0)
 		ev = g_enum_get_value (class, MONKEY_MEDIA_STREAM_INFO_FIELD_TITLE);
@@ -319,10 +416,23 @@
 	{
 		char *tmp = g_strconcat ("audio_", pspec->name, NULL);
 		ev = g_enum_get_value_by_nick (class, tmp);
+
 		g_free (tmp);
 	}
 	/* FIXME end hack */
 
+#if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 6
+	if (G_VALUE_TYPE (value) == G_TYPE_STRING
+		&& g_value_get_string (value) != NULL)
+	{
+		char *u8value = monkey_media_unicodify (g_value_get_string (value));
+		if (u8value)
+			g_value_set_string_take_ownership (value, u8value);
+		else
+			ev = NULL;
+	}
+#endif	
+
 	if (ev != NULL)
 	{
 		MonkeyMediaPlayerSignal *signal;
@@ -341,6 +451,35 @@
 	g_type_class_unref (class);
 }
 
+#if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 6
+static void
+queue_full_cb (GstQueue *queue,
+	       int level,
+	       gpointer data)
+#else
+static void
+queue_full_cb (GstQueue *queue,
+	       gpointer data)     
+#endif
+{
+	MonkeyMediaPlayer *mp = MONKEY_MEDIA_PLAYER (data);
+
+	monkey_media_debug ("got queue full signal, buffering: %s pipeline state: %s, waiting bin state: %s",
+			    mp->priv->use_buffer ? "TRUE" : "FALSE",
+			    gst_element_state_get_name (gst_element_get_state (mp->priv->pipeline)),
+			    gst_element_state_get_name (gst_element_get_state (mp->priv->waiting_bin)));
+	if (!mp->priv->queue_full_blocked)
+		g_signal_handlers_block_by_func (G_OBJECT (mp->priv->queue),
+						 G_CALLBACK (queue_full_cb),
+						 mp);
+	mp->priv->queue_full_blocked = TRUE;
+	monkey_media_debug ("GO GO GO\n");
+	gst_element_set_state (mp->priv->waiting_bin, GST_STATE_PLAYING);
+	monkey_media_debug ("queueing buffering end idle signal\n");
+	g_object_ref (G_OBJECT (mp));
+	g_idle_add ((GSourceFunc) buffering_end_signal_idle, mp);
+}
+
 static void
 monkey_media_player_construct (MonkeyMediaPlayer *mp,
 			       GError **error)
@@ -348,8 +487,7 @@
 	GstDParamManager *dpman;
 
 	/* playback pipeline */
-	mp->priv->pipeline = gst_thread_new ("pipeline");
-
+	mp->priv->pipeline = gst_element_factory_make ("pipeline", "pipeline");
 	g_signal_connect (G_OBJECT (mp->priv->pipeline),
 			  "deep_notify",
 			  G_CALLBACK (deep_notify_cb),
@@ -371,8 +509,9 @@
 			     _("Failed to create gnomevfssrc input element; check your installation"));
 		return;
 	}
+	g_object_ref (mp->priv->src);
 
-	mp->priv->audiocd_src = gst_element_factory_make ("cdparanoia", "src");
+	mp->priv->audiocd_src = gst_element_factory_make ("cdparanoia", "audiosrc");
 	if (mp->priv->audiocd_src == NULL) {
 		g_set_error (error,
 			     MONKEY_MEDIA_PLAYER_ERROR,
@@ -383,6 +522,7 @@
 
 		return;
 	}
+	g_object_ref (mp->priv->audiocd_src);
 
 	g_object_set (G_OBJECT (mp->priv->audiocd_src),
 		      "paranoia-mode",
@@ -393,18 +533,31 @@
 		      monkey_media_get_cd_drive (),
 		      NULL);
 
-	mp->priv->decoder = gst_element_factory_make ("spider", "autoplugger");
-	if (mp->priv->decoder == NULL) {
+	mp->priv->queue = gst_element_factory_make ("queue", "queue");
+	if (mp->priv->queue == NULL) {
 		g_set_error (error,
 			     MONKEY_MEDIA_PLAYER_ERROR,
-			     MONKEY_MEDIA_PLAYER_ERROR_NO_DEMUX_PLUGIN,
-			     _("Failed to create spider element; check your installation"));
+			     MONKEY_MEDIA_PLAYER_ERROR_NO_QUEUE_PLUGIN,
+			     _("Failed to create queue element; check your installation"));
 
 		gst_object_unref (GST_OBJECT (mp->priv->src));
 		gst_object_unref (GST_OBJECT (mp->priv->audiocd_src));
 
 		return;
 	}
+#if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 6
+	g_signal_connect (G_OBJECT (mp->priv->queue), "high_watermark",
+			  G_CALLBACK (queue_full_cb), mp);
+#else
+	g_signal_connect (G_OBJECT (mp->priv->queue), "full",
+			  G_CALLBACK (queue_full_cb), mp);
+#endif	
+	if (!mp->priv->queue_full_blocked)
+		g_signal_handlers_block_by_func (G_OBJECT (mp->priv->queue),
+						 G_CALLBACK (queue_full_cb),
+						 mp);
+	mp->priv->queue_full_blocked = TRUE;
+ 	mp->priv->srcthread = gst_thread_new ("srcthread");
 
 	mp->priv->volume = gst_element_factory_make ("volume", "volume");
 	if (mp->priv->volume == NULL) {
@@ -415,7 +568,7 @@
 
 		gst_object_unref (GST_OBJECT (mp->priv->src));
 		gst_object_unref (GST_OBJECT (mp->priv->audiocd_src));
-		gst_object_unref (GST_OBJECT (mp->priv->decoder));
+		gst_object_unref (GST_OBJECT (mp->priv->queue));
 
 		return;
 	}
@@ -435,20 +588,24 @@
 
 		gst_object_unref (GST_OBJECT (mp->priv->src));
 		gst_object_unref (GST_OBJECT (mp->priv->audiocd_src));
-		gst_object_unref (GST_OBJECT (mp->priv->decoder));
+		gst_object_unref (GST_OBJECT (mp->priv->queue));
 		gst_object_unref (GST_OBJECT (mp->priv->volume));
 
 		return;
 	}
 
-	gst_bin_add_many (GST_BIN (mp->priv->pipeline),
-			  mp->priv->src, mp->priv->decoder,
+        mp->priv->waiting_bin = gst_element_factory_make ("thread", "waiting_bin");
+	gst_bin_add_many (GST_BIN (mp->priv->srcthread),
+			  mp->priv->src, mp->priv->queue, NULL);
+	gst_bin_add_many (GST_BIN (mp->priv->waiting_bin),
 			  mp->priv->volume, mp->priv->sink, NULL);
-	gst_element_link_many (mp->priv->src, mp->priv->decoder,
-			       mp->priv->volume, mp->priv->sink, NULL);
+	gst_bin_add_many (GST_BIN (mp->priv->pipeline),
+			  mp->priv->srcthread, mp->priv->waiting_bin, NULL);
+
+	gst_element_link (mp->priv->src, mp->priv->queue);
+	gst_element_link (mp->priv->volume, mp->priv->sink);
 
-	/* FIXME on the sink doesnt work? */
-	g_signal_connect (G_OBJECT (mp->priv->volume), "eos",
+	g_signal_connect (G_OBJECT (mp->priv->sink), "eos",
 			  G_CALLBACK (eos_cb), mp);
 
 	g_object_set (G_OBJECT (mp->priv->volume_dparam),
@@ -460,6 +617,8 @@
 
 	mp->priv->mute = FALSE;
 	mp->priv->cur_volume = 1.0;
+
+	mp->priv->have_sane_pipeline = FALSE;
 }
 
 MonkeyMediaPlayer *
@@ -490,13 +649,49 @@
 }
 
 void
+monkey_media_player_sync_pipeline (MonkeyMediaPlayer *mp)
+{
+	if (mp->priv->playing) {
+		if (mp->priv->use_buffer) {
+			if (mp->priv->queue_full_blocked)
+			{
+				monkey_media_debug ("unblocking queue full handler");
+				g_signal_handlers_unblock_by_func (G_OBJECT (mp->priv->queue),
+								   G_CALLBACK (queue_full_cb),
+								   mp);
+				mp->priv->queue_full_blocked = FALSE;
+			}
+			monkey_media_debug ("queueing buffering idle signal\n");
+			g_object_ref (G_OBJECT (mp));
+			g_idle_add ((GSourceFunc) buffering_begin_signal_idle, mp);
+			monkey_media_debug ("setting srcthread to PLAYING\n");
+			gst_element_set_state (mp->priv->srcthread,
+					       GST_STATE_PLAYING);
+		} else {
+			monkey_media_debug ("setting pipeline to PLAYING\n");
+			gst_element_set_state (mp->priv->pipeline,
+					       GST_STATE_PLAYING);
+		}
+	} else {
+		monkey_media_debug ("pausing pipeline\n");
+		gst_element_set_state (mp->priv->pipeline,
+				       GST_STATE_PAUSED);
+	}
+}
+
+void
 monkey_media_player_open (MonkeyMediaPlayer *mp,
 			  const char *uri,
+			  gboolean use_buffer,
 			  GError **error)
 {
-	gboolean iradio_mode;
+	gboolean had_sane_pipeline, iradio_mode;
 
 	g_return_if_fail (MONKEY_MEDIA_IS_PLAYER (mp));
+	monkey_media_debug ("opening: %s\n", uri);
+
+	had_sane_pipeline = mp->priv->have_sane_pipeline;
+	mp->priv->have_sane_pipeline = FALSE;
 
 	gst_element_set_state (mp->priv->pipeline,
 			       GST_STATE_NULL);
@@ -505,14 +700,37 @@
 	mp->priv->uri = NULL;
 
 	if (uri == NULL) {
+		GValue value = {0,};
+		g_value_set_string (&value, NULL);
 		g_object_set (G_OBJECT (mp->priv->src),
-			      "location", NULL, NULL);
+			      "location", &value, NULL);
 
 		mp->priv->playing = FALSE;
 
 		return;
 	}
 
+	if (had_sane_pipeline)
+	{
+		gst_element_unlink_many (mp->priv->queue, mp->priv->decoder,
+					 mp->priv->volume, NULL);
+		gst_bin_remove (GST_BIN (mp->priv->waiting_bin),
+				mp->priv->decoder);
+	}
+	
+	mp->priv->decoder = gst_element_factory_make ("spider", "autoplugger");
+	if (mp->priv->decoder == NULL) {
+			g_set_error (error,
+				     MONKEY_MEDIA_PLAYER_ERROR,
+				     MONKEY_MEDIA_PLAYER_ERROR_NO_DEMUX_PLUGIN,
+				     _("Failed to create spider element; check your installation"));
+			return;
+	}
+	gst_bin_add (GST_BIN (mp->priv->waiting_bin),
+		     mp->priv->decoder);
+	gst_element_link_many (mp->priv->queue, mp->priv->decoder, mp->priv->volume, NULL);
+
+#ifdef HAVE_AUDIOCD
 	if (!strncmp ("audiocd://", uri, 10)) {
 		GstEvent *event;
 		int tracknum;
@@ -526,14 +744,12 @@
 		}
 
 		if (!mp->priv->audiocd_mode) {
-			gst_element_unlink (mp->priv->decoder, mp->priv->volume);
-			gst_bin_remove (GST_BIN (mp->priv->pipeline),
+			gst_element_unlink (mp->priv->src, mp->priv->queue);
+			gst_bin_remove (GST_BIN (mp->priv->srcthread),
 					mp->priv->src);
-			gst_bin_remove (GST_BIN (mp->priv->pipeline),
-					mp->priv->decoder);
-			gst_bin_add (GST_BIN (mp->priv->pipeline),
+			gst_bin_add (GST_BIN (mp->priv->srcthread),
 				     mp->priv->audiocd_src);
-			gst_element_link (mp->priv->audiocd_src, mp->priv->volume);
+			gst_element_link (mp->priv->audiocd_src, mp->priv->queue);
 
 			mp->priv->audiocd_mode = TRUE;
 		}
@@ -553,36 +769,35 @@
 		gst_element_send_event (mp->priv->sink, event);
 	} else {
 		if (mp->priv->audiocd_mode) {
-			gst_element_unlink (mp->priv->audiocd_src, mp->priv->volume);
-			gst_bin_remove (GST_BIN (mp->priv->pipeline),
+			gst_element_unlink (mp->priv->audiocd_src, mp->priv->queue);
+			gst_bin_remove (GST_BIN (mp->priv->srcthread),
 					mp->priv->audiocd_src);
-			gst_bin_add_many (GST_BIN (mp->priv->pipeline),
-					  mp->priv->src,
-					  mp->priv->decoder,
-					  NULL);
-			gst_element_link (mp->priv->decoder, mp->priv->volume);
+			gst_bin_add (GST_BIN (mp->priv->srcthread),
+				     mp->priv->src);
+			gst_element_link (mp->priv->src, mp->priv->queue);
 
 			mp->priv->audiocd_mode = FALSE;
 		}
+#endif
 
 		/* Internet radio support */
 		iradio_mode = !strncmp ("http", uri, 4);
 		g_object_set (G_OBJECT (mp->priv->src),
 			      "iradio-mode", iradio_mode, NULL);
-
+		
 		g_object_set (G_OBJECT (mp->priv->src),
 			      "location", uri, NULL);
+#ifdef HAVE_AUDIOCD
 	}
-
+#endif
+	
 	mp->priv->uri = g_strdup (uri);
 
-	if (mp->priv->playing) {
-		gst_element_set_state (mp->priv->pipeline,
-				       GST_STATE_PLAYING);
-	} else {
-		gst_element_set_state (mp->priv->pipeline,
-				       GST_STATE_PAUSED);
-	}
+	mp->priv->use_buffer = use_buffer;
+
+	monkey_media_player_sync_pipeline (mp);
+	
+	mp->priv->have_sane_pipeline = TRUE;
 }
 
 void
@@ -590,11 +805,15 @@
 {
 	g_return_if_fail (MONKEY_MEDIA_IS_PLAYER (mp));
 
+	monkey_media_debug ("closing\n");
 	mp->priv->playing = FALSE;
 
 	g_free (mp->priv->uri);
 	mp->priv->uri = NULL;
 
+	if (!mp->priv->have_sane_pipeline)
+		return;
+
 	gst_element_set_state (mp->priv->pipeline,
 			       GST_STATE_NULL);
 
@@ -617,8 +836,11 @@
 
 	mp->priv->playing = TRUE;
 
-	gst_element_set_state (mp->priv->pipeline,
-			       GST_STATE_PLAYING);
+	if (!mp->priv->have_sane_pipeline)
+		return;
+
+	monkey_media_debug ("playing\n");
+	monkey_media_player_sync_pipeline (mp);
 }
 
 void
@@ -626,8 +848,15 @@
 {
 	g_return_if_fail (MONKEY_MEDIA_IS_PLAYER (mp));
 
+	if (!mp->priv->playing)
+		return;
+
 	mp->priv->playing = FALSE;
 
+	if (!mp->priv->have_sane_pipeline)
+		return;
+
+	monkey_media_debug ("pausing\n");
 	gst_element_set_state (mp->priv->pipeline,
 			       GST_STATE_PAUSED);
 }
@@ -647,6 +876,7 @@
 	g_return_if_fail (MONKEY_MEDIA_IS_PLAYER (mp));
 	g_return_if_fail (volume >= 0.0 && volume <= 1.0);
 
+	monkey_media_debug ("setting volume to %f\n", volume);
 	g_object_set (G_OBJECT (mp->priv->volume_dparam),
 		      "value_float",
 		      volume,
@@ -660,6 +890,7 @@
 {
 	g_return_val_if_fail (MONKEY_MEDIA_IS_PLAYER (mp), 0.0);
 
+	monkey_media_debug ("current volume is %f\n", mp->priv->cur_volume);
 	return mp->priv->cur_volume;
 }
 
@@ -690,6 +921,9 @@
 {
 	g_return_val_if_fail (MONKEY_MEDIA_IS_PLAYER (mp), FALSE);
 
+	if (!mp->priv->have_sane_pipeline)
+		return FALSE;
+
 	/* FIXME we're lying here, no idea how to fix this though, without trying
 	 * a seek which might disrupt playback */
 	return TRUE;
@@ -704,6 +938,10 @@
 	g_return_if_fail (MONKEY_MEDIA_IS_PLAYER (mp));
 	g_return_if_fail (time >= 0);
 
+	if (!mp->priv->have_sane_pipeline)
+		return;
+
+	monkey_media_debug ("seeking to %ld\n", time);
 	gst_element_set_state (mp->priv->pipeline, GST_STATE_PAUSED);
 
 	event = gst_event_new_seek (GST_FORMAT_TIME |
@@ -719,12 +957,17 @@
 monkey_media_player_get_time (MonkeyMediaPlayer *mp)
 {
 	GstClock *clock;
+	long ret;
 
 	g_return_val_if_fail (MONKEY_MEDIA_IS_PLAYER (mp), -1);
 
+	if (!mp->priv->have_sane_pipeline)
+		return -1;
+
 	clock = gst_bin_get_clock (GST_BIN (mp->priv->pipeline));
+	ret = (long) (gst_clock_get_time (clock) / GST_SECOND);
 
-	return (long) (gst_clock_get_time (clock) / GST_SECOND);
+	return ret;
 }
 
 #endif /* HAVE_GSTREAMER */
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/monkey-media-player.h net-rhythmbox/monkey-media/monkey-media-player.h
--- monkey-media/src/monkey-media-player.h	2003-04-11 17:55:14.000000000 -0400
+++ net-rhythmbox/monkey-media/monkey-media-player.h	2003-04-17 05:09:57.000000000 -0400
@@ -30,6 +30,7 @@
 typedef enum
 {
 	MONKEY_MEDIA_PLAYER_ERROR_NO_INPUT_PLUGIN,
+	MONKEY_MEDIA_PLAYER_ERROR_NO_QUEUE_PLUGIN,
 	MONKEY_MEDIA_PLAYER_ERROR_NO_DEMUX_PLUGIN,
 	MONKEY_MEDIA_PLAYER_ERROR_NO_VOLUME_PLUGIN,
 	MONKEY_MEDIA_PLAYER_ERROR_DEMUX_FAILED,
@@ -65,6 +66,8 @@
 	void (*eos)   (MonkeyMediaPlayer *mp);
 	void (*info)  (MonkeyMediaPlayer *mp, MonkeyMediaStreamInfoField field,
 		       GValue *value);
+	void (*buffering_begin) (MonkeyMediaPlayer *mp);
+	void (*buffering_end) (MonkeyMediaPlayer *mp);
 	void (*error) (MonkeyMediaPlayer *mp, GError *error);
 	void (*tick)  (MonkeyMediaPlayer *mp, long elapsed);
 } MonkeyMediaPlayerClass;
@@ -75,6 +78,7 @@
 
 void               monkey_media_player_open       (MonkeyMediaPlayer *mp,
 						   const char *uri,
+						   gboolean use_buffer,
 		                                   GError **error);
 
 const char	  *monkey_media_player_get_uri    (MonkeyMediaPlayer *mp);
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/stream-info-impl/audiocd-stream-info-impl.c net-rhythmbox/monkey-media/stream-info-impl/audiocd-stream-info-impl.c
--- monkey-media/src/stream-info-impl/audiocd-stream-info-impl.c	2002-10-18 17:29:52.000000000 -0400
+++ net-rhythmbox/monkey-media/stream-info-impl/audiocd-stream-info-impl.c	2003-04-25 18:22:44.000000000 -0400
@@ -27,6 +27,7 @@
 #include "monkey-media-private.h"
 #include "monkey-media-audio-cd-private.h"
 #include "monkey-media-musicbrainz.h"
+#include "monkey-media-debug.h"
 
 #include "audiocd-stream-info-impl.h"
 
@@ -168,8 +169,10 @@
 	}
 
 	impl->priv->mb = monkey_media_musicbrainz_new ();
+	g_return_if_fail (impl->priv->mb != NULL);
 
 	impl->priv->cd_id = monkey_media_audio_cd_get_disc_id (impl->priv->cd, &error);
+	monkey_media_debug ("Calculated MusicBrainz ID \"%s\"", impl->priv->cd_id);
 	if (error != NULL)
 	{
 		g_object_set (G_OBJECT (info), "error", error, NULL);
@@ -180,6 +183,7 @@
 					        MONKEY_MEDIA_MUSICBRAINZ_QUERY_CD,
 					        impl->priv->cd_id) == FALSE)
 	{
+		monkey_media_debug ("MusicBrainz MONKEY_MEDIA_MUSICBRAINZ_QUERY_CD failed");
 		/* no disc found, let's try single song lookup mode */
 		GValue value = { 0, };
 
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/stream-info-impl/id3-vfs/Makefile.am net-rhythmbox/monkey-media/stream-info-impl/id3-vfs/Makefile.am
--- monkey-media/src/stream-info-impl/id3-vfs/Makefile.am	2002-05-27 15:25:04.000000000 -0400
+++ net-rhythmbox/monkey-media/stream-info-impl/id3-vfs/Makefile.am	2003-02-09 03:10:29.000000000 -0500
@@ -7,5 +7,5 @@
 INCLUDES = 						\
         -DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
 	-DG_LOG_DOMAIN=\"MonkeyMedia\"			\
-	-I$(top_srcdir) 				\
-	$(MONKEYMEDIA_CFLAGS)
+	-I$(top_srcdir)/monkey-media			\
+	$(RHYTHMBOX_CFLAGS)
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/stream-info-impl/Makefile.am net-rhythmbox/monkey-media/stream-info-impl/Makefile.am
--- monkey-media/src/stream-info-impl/Makefile.am	2003-01-08 16:57:08.000000000 -0500
+++ net-rhythmbox/monkey-media/stream-info-impl/Makefile.am	2003-04-25 16:28:47.000000000 -0400
@@ -7,19 +7,23 @@
 	vorbis-stream-info-impl.h			\
 	mp3-stream-info-impl.c				\
 	mp3-stream-info-impl.h				\
-	flac-stream-info-impl.c				\
-	flac-stream-info-impl.h				\
-	audiocd-stream-info-impl.c			\
-	audiocd-stream-info-impl.h			\
 	ogg-helper.c					\
 	ogg-helper.h
 
+if ENABLE_FLAC
+libstream_info_impl_la_SOURCES += flac-stream-info-impl.c flac-stream-info-impl.h
+endif
+
+if AUDIOCD
+libstream_info_impl_la_SOURCES += audiocd-stream-info-impl.c audiocd-stream-info-impl.h
+endif
+
 libstream_info_impl_la_LIBADD =				\
-	$(top_builddir)/src/stream-info-impl/id3-vfs/libid3-vfs.la -lFLAC
+	$(top_builddir)/monkey-media/stream-info-impl/id3-vfs/libid3-vfs.la -lFLAC
 
 INCLUDES = 						\
         -DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
 	-DG_LOG_DOMAIN=\"MonkeyMedia\"			\
 	-I$(top_srcdir) 				\
-	-I$(top_srcdir)/src 				\
-	$(MONKEYMEDIA_CFLAGS)
+	-I$(top_srcdir)/monkey-media 			\
+	$(RHYTHMBOX_CFLAGS)
diff -x .deps -x '*.la' -x Makefile -x Makefile.in -x 'Rhythmbox*' -x '*.P' -x '*.o' -x '*.lo' -x '*.Plo' -x '*.al' -x configure -x po -x CVS -x 'monkey-media-marshal*' -uNr monkey-media/src/stream-info-impl/vorbis-stream-info-impl.c net-rhythmbox/monkey-media/stream-info-impl/vorbis-stream-info-impl.c
--- monkey-media/src/stream-info-impl/vorbis-stream-info-impl.c	2003-01-30 13:45:58.000000000 -0500
+++ net-rhythmbox/monkey-media/stream-info-impl/vorbis-stream-info-impl.c	2003-04-17 05:07:06.000000000 -0400
@@ -3,6 +3,7 @@
  *                     Marco Pesenti Gritti <marco@it.gnome.org>
  *                     Bastien Nocera <hadess@hadess.net>
  *                     Seth Nickell <snickell@stanford.edu>
+ *  Copyright (C) 2003,2003 Colin Walters <walters@debian.org>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]