changeset 2331:5b37a9f2d0cc

Branch merge
author Sascha Hlusiak <contact@saschahlusiak.de>
date Sun, 20 Jan 2008 14:53:47 +0100
parents 0be42d832217 (current diff) 7b38e28464ee (diff)
children 31754b406cd6 0dd31200a33a
files
diffstat 16 files changed, 664 insertions(+), 500 deletions(-) [+]
line wrap: on
line diff
--- a/src/cdaudio-ng/cdaudio-ng.c	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/cdaudio-ng/cdaudio-ng.c	Sun Jan 20 14:53:47 2008 +0100
@@ -2,7 +2,7 @@
  * Audacious CD Digital Audio plugin
  *
  * Copyright (c) 2007 Calin Crisan <ccrisan@gmail.com>
- * 
+ *
  * 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; under version 3 of the License.
@@ -16,10 +16,6 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses>.
  */
 
-/* TODO (added by ccr)
- * - maybe make CDDB lib optional?
- * - use_cddb/use_cdtext don't seem to be checked in all necessary places. why?
- */
 
 #include "config.h"
 
@@ -51,22 +47,23 @@
 #include "cdaudio-ng.h"
 #include "configure.h"
 
-struct cdng_cfg_t		cdng_cfg;
-static gint				firsttrackno = -1;
-static gint				lasttrackno = -1;
-static CdIo_t			*pcdio = NULL;
-static trackinfo_t		*trackinfo = NULL;
-static gboolean			is_paused = FALSE;
-static gint				playing_track = -1;
-static dae_params_t		*pdae_params = NULL;
-static InputPlayback	*pglobalinputplayback = NULL;
-static GtkWidget		*main_menu_item, *playlist_menu_item;
+
+struct cdng_cfg_t			cdng_cfg;
+static gint					firsttrackno = -1;
+static gint					lasttrackno = -1;
+static CdIo_t				*pcdio = NULL;
+static trackinfo_t			*trackinfo = NULL;
+static gboolean				is_paused = FALSE;
+static gint					playing_track = -1;
+static dae_params_t			*pdae_params = NULL;
+static InputPlayback		*pglobalinputplayback = NULL;
+static GtkWidget			*main_menu_item, *playlist_menu_item;
+static GThread				*scan_cd_thread = NULL;
 
 static void			cdaudio_init(void);
 static void			cdaudio_about(void);
 static void			cdaudio_configure(void);
 static gint			cdaudio_is_our_file(gchar *filename);
-static GList		*cdaudio_scan_dir(gchar *dirname);
 static void			cdaudio_play_file(InputPlayback *pinputplayback);
 static void			cdaudio_stop(InputPlayback *pinputplayback);
 static void			cdaudio_pause(InputPlayback *pinputplayback, gshort paused);
@@ -79,8 +76,11 @@
 static Tuple		*cdaudio_get_song_tuple(gchar *filename);
 
 static void			menu_click(void);
-static Tuple		*create_tuple_from_trackinfo(gchar *filename);
+static Tuple		*create_tuple_from_trackinfo_and_filename(gchar *filename);
+static Tuple		*create_tuple_from_trackinfo(int trackno);
 static void			dae_play_loop(dae_params_t *pdae_params);
+static void			*scan_cd(void *nothing);
+static void			scan_cd_threaded();
 static gint			calculate_track_length(gint startlsn, gint endlsn);
 static gint			find_trackno_from_filename(gchar *filename);
 static void			cleanup_on_error(void);
@@ -92,7 +92,7 @@
 	.about = cdaudio_about,
 	.configure = cdaudio_configure,
 	.is_our_file = cdaudio_is_our_file,
-	.scan_dir = cdaudio_scan_dir,
+//	.scan_cd = cdaudio_scan_cd,
 	.play_file = cdaudio_play_file,
 	.stop = cdaudio_stop,
 	.pause = cdaudio_pause,
@@ -136,9 +136,9 @@
 {
 	ConfigDb *db;
 	gchar *menu_item_text;
-	
+
 	debug("cdaudio_init()\n");
-	
+
 	memset(&cdng_cfg, 0, sizeof(cdng_cfg));
 
 	if ((db = aud_cfg_db_open()) == NULL) {
@@ -159,7 +159,7 @@
 	if (!aud_cfg_db_get_bool(db, "CDDA", "use_dae", &cdng_cfg.use_dae))
 	*/
 	cdng_cfg.use_dae = TRUE;
-		
+
 	if (!aud_cfg_db_get_int(db, "CDDA", "limitspeed", &cdng_cfg.limitspeed))
 		cdng_cfg.limitspeed = 1;
 	if (!aud_cfg_db_get_bool(db, "CDDA", "use_cdtext", &cdng_cfg.use_cdtext))
@@ -177,7 +177,7 @@
 	if (!aud_cfg_db_get_bool(db, "CDDA", "debug", &cdng_cfg.debug))
 		cdng_cfg.debug = FALSE;
 	if (!aud_cfg_db_get_bool(db, "audacious", "use_proxy", &cdng_cfg.use_proxy))
-		cdng_cfg.debug = FALSE;
+		cdng_cfg.use_proxy = FALSE;
 	if (!aud_cfg_db_get_string(db, "audacious", "proxy_host", &cdng_cfg.proxy_host))
 		cdng_cfg.proxy_host = g_strdup("");
 	if (!aud_cfg_db_get_int(db, "audacious", "proxy_port", &cdng_cfg.proxy_port))
@@ -200,14 +200,14 @@
 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(main_menu_item), gtk_image_new_from_stock(GTK_STOCK_CDROM, GTK_ICON_SIZE_MENU));
 	gtk_widget_show(main_menu_item);
 	audacious_menu_plugin_item_add(AUDACIOUS_MENU_MAIN, main_menu_item);
-	g_signal_connect(G_OBJECT(main_menu_item), "button_press_event", G_CALLBACK(menu_click), NULL);  
-	
+	g_signal_connect(G_OBJECT(main_menu_item), "button_press_event", G_CALLBACK(menu_click), NULL);
+
 	playlist_menu_item = gtk_image_menu_item_new_with_label(menu_item_text);
 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(playlist_menu_item), gtk_image_new_from_stock(GTK_STOCK_CDROM, GTK_ICON_SIZE_MENU));
 	gtk_widget_show(playlist_menu_item);
 	audacious_menu_plugin_item_add(AUDACIOUS_MENU_PLAYLIST, playlist_menu_item);
-	g_signal_connect(G_OBJECT(playlist_menu_item), "button_press_event", G_CALLBACK(menu_click), NULL);  
-	
+	g_signal_connect(G_OBJECT(playlist_menu_item), "button_press_event", G_CALLBACK(menu_click), NULL);
+
 	aud_uri_set_plugin("cdda://", &inputplugin);
 }
 
@@ -255,19 +255,13 @@
 		/* no CD information yet */
 		if (pcdio == NULL) {
 			debug("no CD information, scanning\n");
-			cdaudio_scan_dir(CDDA_DEFAULT);
+			scan_cd_threaded(NULL);
 		}
 
 		/* reload the cd information if the media has changed */
-		if (cdio_get_media_changed(pcdio) && pcdio != NULL) {
+		if (pcdio != NULL && cdio_get_media_changed(pcdio)) {
 			debug("CD changed, rescanning\n");
-			if (cdaudio_scan_dir(CDDA_DEFAULT) == NULL)
-				pcdio = NULL;
-		}
-
-		if (pcdio == NULL) {
-			debug("\"%s\" is not our file\n", filename);
-			return FALSE;
+			scan_cd_threaded(NULL);
 		}
 
 		/* check if the requested track actually exists on the current audio cd */
@@ -276,7 +270,7 @@
 			debug("\"%s\" is not our file\n", filename);
 			return FALSE;
 		}
-		
+
 		debug("\"%s\" is our file\n", filename);
 		return TRUE;
 	}
@@ -306,258 +300,30 @@
 }
 
 
-static GList *cdaudio_scan_dir(gchar *dirname)
-{
-	gint trackno;
-
-	debug("cdaudio_scan_dir(\"%s\")\n", dirname);
-
-	/* if the given dirname does not belong to us, we return NULL */
-	if (strstr(dirname, CDDA_DEFAULT) == NULL) {
-		debug("\"%s\" directory does not belong to us\n", dirname);
-		return NULL;
-	}
-
-	/* find an available, audio capable, cd drive  */
-	if (cdng_cfg.device != NULL && strlen(cdng_cfg.device) > 0) {
-		pcdio = cdio_open(cdng_cfg.device, DRIVER_UNKNOWN);
-		if (pcdio == NULL) {
-			cdaudio_error("Failed to open CD device \"%s\".\n", cdng_cfg.device);
-			return NULL;
-		}
-	}
-	else {
-		gchar **ppcd_drives = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false);
-		pcdio = NULL;
-		if (ppcd_drives != NULL && *ppcd_drives != NULL) { /* we have at least one audio capable cd drive */
-			pcdio = cdio_open(*ppcd_drives, DRIVER_UNKNOWN);
-			if (pcdio == NULL) {
-				cdaudio_error("Failed to open CD.\n");
-				cleanup_on_error();
-				return NULL;
-			}
-			debug("found cd drive \"%s\" with audio capable media\n", *ppcd_drives);
-		}
-		else {
-			cdaudio_error("Unable to find or access a CDDA capable drive.\n");
-			cleanup_on_error();
-			return NULL;
-		}
-		if (ppcd_drives != NULL && *ppcd_drives != NULL)
-			cdio_free_device_list(ppcd_drives);
-	}
-
-	/* limit read speed */
-	if (cdng_cfg.limitspeed > 0 && cdng_cfg.use_dae) {
-		debug("setting drive speed limit to %dx\n", cdng_cfg.limitspeed);
-		if (cdio_set_speed(pcdio, cdng_cfg.limitspeed) != DRIVER_OP_SUCCESS)
-			cdaudio_error("Failed to set drive speed to %dx.\n", cdng_cfg.limitspeed);
-	}
-
-	/* get general track initialization */
-	cdrom_drive_t *pcdrom_drive = cdio_cddap_identify_cdio(pcdio, 1, NULL);	// todo : check return / NULL
-	firsttrackno = cdio_get_first_track_num(pcdrom_drive->p_cdio);
-	lasttrackno = cdio_get_last_track_num(pcdrom_drive->p_cdio);
-	if (firsttrackno == CDIO_INVALID_TRACK || lasttrackno == CDIO_INVALID_TRACK) {
-		cdaudio_error("Failed to retrieve first/last track number.\n");
-		cleanup_on_error();
-		return NULL;
-	}
-	debug("first track is %d and last track is %d\n", firsttrackno, lasttrackno);
-
-	g_free(trackinfo);
-	trackinfo = (trackinfo_t *) g_new(trackinfo_t, (lasttrackno + 1));
-
-	cdaudio_set_fullinfo(&trackinfo[0],
-		cdio_get_track_lsn(pcdrom_drive->p_cdio, 0),
-		cdio_get_track_last_lsn(pcdrom_drive->p_cdio, CDIO_CDROM_LEADOUT_TRACK),
-		"", "", "");
-	
-	for (trackno = firsttrackno; trackno <= lasttrackno; trackno++) {
-		cdaudio_set_fullinfo(&trackinfo[trackno],
-			cdio_get_track_lsn(pcdrom_drive->p_cdio, trackno),
-			cdio_get_track_last_lsn(pcdrom_drive->p_cdio, trackno),
-			"", "", "");
-		
-		if (trackinfo[trackno].startlsn == CDIO_INVALID_LSN || trackinfo[trackno].endlsn == CDIO_INVALID_LSN) {
-			cdaudio_error("Failed to retrieve stard/end lsn for track %d.\n", trackno);
-			cleanup_on_error();
-			return NULL;
-		}
-	}
-
-	/* initialize de cddb subsystem */
-	cddb_conn_t *pcddb_conn = NULL;
-	cddb_disc_t *pcddb_disc = NULL;
-	cddb_track_t *pcddb_track = NULL;
-
-	if (cdng_cfg.use_cddb) {
-		pcddb_conn = cddb_new();
-		if (pcddb_conn == NULL)
-			cdaudio_error("Failed to create the cddb connection.\n");
-		else {
-			debug("getting CDDB info\n");
-
-			if (cdng_cfg.use_proxy) {
-				cddb_http_proxy_enable(pcddb_conn);
-				cddb_set_http_proxy_server_name(pcddb_conn, cdng_cfg.proxy_host);
-				cddb_set_http_proxy_server_port(pcddb_conn, cdng_cfg.proxy_port);
-				cddb_set_http_proxy_username(pcddb_conn, cdng_cfg.proxy_username);
-				cddb_set_http_proxy_password(pcddb_conn, cdng_cfg.proxy_password);
-				cddb_set_server_name(pcddb_conn, cdng_cfg.cddb_server);
-				cddb_set_server_port(pcddb_conn, cdng_cfg.cddb_port);
-			}
-			else
-				if (cdng_cfg.cddb_http) {
-					cddb_http_enable(pcddb_conn);
-					cddb_set_server_name(pcddb_conn, cdng_cfg.cddb_server);
-					cddb_set_server_port(pcddb_conn, cdng_cfg.cddb_port);
-				}
-				else {
-					cddb_set_server_name(pcddb_conn, cdng_cfg.cddb_server);
-					cddb_set_server_port(pcddb_conn, cdng_cfg.cddb_port);
-				}
-
-			pcddb_disc = cddb_disc_new();
-			for (trackno = firsttrackno; trackno <= lasttrackno; trackno++) {
-				pcddb_track = cddb_track_new();
-				cddb_track_set_frame_offset(pcddb_track, trackinfo[trackno].startlsn);
-				cddb_disc_add_track(pcddb_disc, pcddb_track);
-			}
-
-			msf_t startmsf, endmsf;
-			cdio_get_track_msf(pcdio, 1, &startmsf);
-			cdio_get_track_msf(pcdio, CDIO_CDROM_LEADOUT_TRACK, &endmsf);
-			cddb_disc_set_length(pcddb_disc, cdio_audio_get_msf_seconds(&endmsf) - cdio_audio_get_msf_seconds(&startmsf));
-			
-			cddb_disc_calc_discid(pcddb_disc);
-			int discid = cddb_disc_get_discid(pcddb_disc);
-			debug("CDDB disc id = %x\n", discid);
-
-			gint matches;
-			if ((matches = cddb_query(pcddb_conn, pcddb_disc)) == -1) {
-				if (cddb_errno(pcddb_conn) == CDDB_ERR_OK)
-					cdaudio_error("Failed to query the CDDB server\n");
-				else
-					cdaudio_error("Failed to query the CDDB server: %s\n", cddb_error_str(cddb_errno(pcddb_conn)));
-				
-				cddb_disc_destroy(pcddb_disc);
-				pcddb_disc = NULL;
-			}
-			else {
-				if (matches == 0) {
-					debug("no cddb info available for this disc\n");
-					
-					cddb_disc_destroy(pcddb_disc);
-					pcddb_disc = NULL;
-				}
-				else {
-					debug("CDDB disc category = \"%s\"\n", cddb_disc_get_category_str(pcddb_disc));
-
-					cddb_read(pcddb_conn, pcddb_disc);
-					if (cddb_errno(pcddb_conn) != CDDB_ERR_OK) {
-						cdaudio_error("failed to read the cddb info: %s\n", cddb_error_str(cddb_errno(pcddb_conn)));
-						cddb_disc_destroy(pcddb_disc);
-						pcddb_disc = NULL;
-					}
-					else {
-						debug("we have got the cddb info\n");
-						cdaudio_set_strinfo(&trackinfo[0],
-							cddb_disc_get_artist(pcddb_disc),
-							cddb_disc_get_title(pcddb_disc),
-							cddb_disc_get_genre(pcddb_disc));
-					}
-				}
-			}
-		}
-	}
-
-	/* adding trackinfo[0] information (the disc) */
-	if (cdng_cfg.use_cdtext) {
-		debug("getting cd-text information for disc\n");
-		cdtext_t *pcdtext = cdio_get_cdtext(pcdrom_drive->p_cdio, 0);
-		if (pcdtext == NULL || pcdtext->field[CDTEXT_TITLE] == NULL) {
-			debug("no cd-text available for disc\n");
-		}
-		else {
-			cdaudio_set_strinfo(&trackinfo[0],
-				pcdtext->field[CDTEXT_PERFORMER] ? pcdtext->field[CDTEXT_PERFORMER] : "",
-				pcdtext->field[CDTEXT_TITLE] ? pcdtext->field[CDTEXT_TITLE] : "",
-				pcdtext->field[CDTEXT_GENRE] ? pcdtext->field[CDTEXT_GENRE] : "");
-		}
-	}
-
-	/* add track "file" names to the list */
-	GList *list = NULL;
-	for (trackno = firsttrackno; trackno <= lasttrackno; trackno++) {
-		list = g_list_append(list, g_strdup_printf("track%02u.cda", trackno));
-		cdtext_t *pcdtext = NULL;
-		if (cdng_cfg.use_cdtext) {
-			debug("getting cd-text information for track %d\n", trackno);
-			pcdtext = cdio_get_cdtext(pcdrom_drive->p_cdio, trackno);
-			if (pcdtext == NULL || pcdtext->field[CDTEXT_PERFORMER] == NULL) {
-				debug("no cd-text available for track %d\n", trackno);
-				pcdtext = NULL;
-			}
-		}
-
-		if (pcdtext != NULL) {
-			cdaudio_set_strinfo(&trackinfo[trackno],
-				pcdtext->field[CDTEXT_PERFORMER] ? pcdtext->field[CDTEXT_PERFORMER] : "",
-				pcdtext->field[CDTEXT_TITLE] ? pcdtext->field[CDTEXT_TITLE] : "",
-				pcdtext->field[CDTEXT_GENRE] ? pcdtext->field[CDTEXT_GENRE] : "");
-		}
-		else
-			if (pcddb_disc != NULL) {
-				cddb_track_t *pcddb_track = cddb_disc_get_track(pcddb_disc, trackno - 1);
-				cdaudio_set_strinfo(&trackinfo[trackno],
-					cddb_track_get_artist(pcddb_track),
-					cddb_track_get_title(pcddb_track),
-					cddb_disc_get_genre(pcddb_disc));
-			}
-			else {
-				cdaudio_set_strinfo(&trackinfo[trackno], "", "", "");
-				g_snprintf(trackinfo[trackno].name, DEF_STRING_LEN, "CD Audio Track %02u", trackno);
-			}
-	}
-
-	if (cdng_cfg.debug) {
-		debug("disc has : performer = \"%s\", name = \"%s\", genre = \"%s\"\n",
-			trackinfo[0].performer, trackinfo[0].name, trackinfo[0].genre);
-		
-		for (trackno = firsttrackno; trackno <= lasttrackno; trackno++) {
-			debug("track %d has : performer = \"%s\", name = \"%s\", genre = \"%s\", startlsn = %d, endlsn = %d\n",
-				trackno, trackinfo[trackno].performer, trackinfo[trackno].name, trackinfo[trackno].genre, trackinfo[trackno].startlsn, trackinfo[trackno].endlsn);
-		}
-	}
-
-	if (pcddb_disc != NULL)
-		cddb_disc_destroy(pcddb_disc);
-	
-	if (pcddb_conn != NULL)
-		cddb_destroy(pcddb_conn);
-
-	return list;
-}
-
 static void cdaudio_play_file(InputPlayback *pinputplayback)
 {
 	Tuple *tuple;
 	gchar *title;
-	
+
 	debug("cdaudio_play_file(\"%s\")\n", pinputplayback->filename);
 
 	pglobalinputplayback = pinputplayback;
 
 	if (trackinfo == NULL) {
 		debug("no CD information, scanning\n");
-		cdaudio_scan_dir(CDDA_DEFAULT);
+		if (scan_cd_thread != NULL)
+			g_thread_join(scan_cd_thread);
+		else
+			scan_cd(pinputplayback);
 	}
-
-	if (cdio_get_media_changed(pcdio)) {
-		debug("CD changed, rescanning\n");
-		cdaudio_scan_dir(CDDA_DEFAULT);
-	}
+	else
+		if (cdio_get_media_changed(pcdio)) {
+			debug("CD changed, rescanning\n");
+			if (scan_cd_thread != NULL)
+				g_thread_join(scan_cd_thread);
+			else
+				scan_cd(pinputplayback);
+		}
 
 	if (trackinfo == NULL) {
 		debug("no CD information can be retrieved, aborting\n");
@@ -576,7 +342,7 @@
 	playing_track = trackno;
 	is_paused = FALSE;
 
-	tuple = create_tuple_from_trackinfo(pinputplayback->filename);
+	tuple = create_tuple_from_trackinfo_and_filename(pinputplayback->filename);
 	title = aud_tuple_formatter_make_title_string(tuple, aud_get_gentitle_format());
 
 	pinputplayback->set_params(pinputplayback, title, calculate_track_length(trackinfo[trackno].startlsn, trackinfo[trackno].endlsn), 1411200, 44100, 2);
@@ -818,8 +584,8 @@
 {
 	debug("cdaudio_get_song_info(\"%s\")\n", filename);
 
-	gint trackno = find_trackno_from_filename(filename);
-	Tuple *tuple = create_tuple_from_trackinfo(filename);
+	Tuple *tuple = create_tuple_from_trackinfo_and_filename(filename);
+	int trackno = find_trackno_from_filename(filename);
 
 	if (tuple) {
 		*title = aud_tuple_formatter_process_string(tuple, aud_get_gentitle_format());
@@ -833,7 +599,7 @@
 {
 	debug("cdaudio_get_song_tuple(\"%s\")\n", filename);
 
-	return create_tuple_from_trackinfo(filename);
+	return create_tuple_from_trackinfo_and_filename(filename);
 }
 
 
@@ -842,10 +608,21 @@
  */
 static void menu_click()
 {
-    GList *list, *node;
-    gchar *filename;
-	
-	if (!(list = cdaudio_scan_dir(CDDA_DEFAULT))) {
+    gchar filename[DEF_STRING_LEN];
+    gboolean available = TRUE;
+
+	/* reload the cd information if the media has changed, or no track information is available */
+	if (pcdio == NULL || cdio_get_media_changed(pcdio)) {
+		debug("CD changed, rescanning\n");
+
+		available = FALSE;
+		if (scan_cd_thread != NULL)
+			return;
+		else
+			available = (scan_cd(NULL) != NULL);
+	}
+
+	if (!available) {
 	    const gchar *markup =
 	        N_("<b><big>No playable CD found.</big></b>\n\n"
 	           "No CD inserted, or inserted CD is not an audio CD.\n");
@@ -861,17 +638,49 @@
 		return;
 	}
 
-	for (node = list; node; node = g_list_next(node)) {
-		filename = g_build_filename(CDDA_DEFAULT, node->data, NULL);
-		aud_playlist_add(aud_playlist_get_active(), filename);
-		g_free(filename);
-		g_free(node->data);
+	int trackno;
+	for (trackno = firsttrackno; trackno <= lasttrackno; trackno++) {
+		g_snprintf(filename, DEF_STRING_LEN, "track%02u.cda", trackno);
+		gchar *pathname = g_build_filename(CDDA_DEFAULT, filename, NULL);
+		aud_playlist_add(aud_playlist_get_active(), pathname);
+	}
+}
+
+static Tuple *create_tuple_from_trackinfo(int trackno)
+{
+	Tuple *tuple = aud_tuple_new();
+
+	if (trackinfo == NULL)
+		return tuple;
+
+	if (trackno < firsttrackno || trackno > lasttrackno)
+		return tuple;
+
+	if(strlen(trackinfo[trackno].performer)) {
+		aud_tuple_associate_string(tuple, FIELD_ARTIST, NULL, trackinfo[trackno].performer);
+	}
+	if(strlen(trackinfo[0].name)) {
+		aud_tuple_associate_string(tuple, FIELD_ALBUM, NULL, trackinfo[0].name);
+	}
+	if(strlen(trackinfo[trackno].name)) {
+		aud_tuple_associate_string(tuple, FIELD_TITLE, NULL, trackinfo[trackno].name);
 	}
 
-	g_list_free(list);
+	aud_tuple_associate_int(tuple, FIELD_TRACK_NUMBER, NULL, trackno);
+	aud_tuple_associate_string(tuple, -1, "ext", "cda"); //XXX should do? --yaz
+
+	aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL,
+		calculate_track_length(trackinfo[trackno].startlsn, trackinfo[trackno].endlsn));
+
+	if(strlen(trackinfo[trackno].genre)) {
+		aud_tuple_associate_string(tuple, FIELD_GENRE, NULL,  trackinfo[trackno].genre);
+	}
+	//tuple->year = 0; todo: set the year
+
+	return tuple;
 }
 
-static Tuple *create_tuple_from_trackinfo(gchar *filename)
+static Tuple *create_tuple_from_trackinfo_and_filename(gchar *filename)
 {
 	Tuple *tuple = aud_tuple_new_from_filename(filename);
 
@@ -892,13 +701,13 @@
 	if(strlen(trackinfo[trackno].name)) {
 		aud_tuple_associate_string(tuple, FIELD_TITLE, NULL, trackinfo[trackno].name);
 	}
-	
+
 	aud_tuple_associate_int(tuple, FIELD_TRACK_NUMBER, NULL, trackno);
 	aud_tuple_associate_string(tuple, -1, "ext", "cda"); //XXX should do? --yaz
 
 	aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL,
 		calculate_track_length(trackinfo[trackno].startlsn, trackinfo[trackno].endlsn));
-	
+
 	if(strlen(trackinfo[trackno].genre)) {
 		aud_tuple_associate_string(tuple, FIELD_GENRE, NULL,  trackinfo[trackno].genre);
 	}
@@ -950,7 +759,7 @@
 
 		/* compute the actual number of sectors to read */
 		gint lsncount = CDDA_DAE_FRAMES <= (pdae_params->endlsn - pdae_params->currlsn + 1) ? CDDA_DAE_FRAMES : (pdae_params->endlsn - pdae_params->currlsn + 1);
-		
+
 		/* check too see if we have reached the end of the song */
 		if (lsncount <= 0) {
 			sleep(3);
@@ -974,14 +783,14 @@
 		while (pdae_params->pplayback->playing && remainingbytes > 0 && pdae_params->seektime == -1) {
 			/* compute the actual number of bytes to play */
 			gint bytecount = CDIO_CD_FRAMESIZE_RAW <= remainingbytes ? CDIO_CD_FRAMESIZE_RAW : remainingbytes;
-			
+
 			/* wait until the output buffer has enough room */
 			while (pdae_params->pplayback->playing && pdae_params->pplayback->output->buffer_free() < bytecount && pdae_params->seektime == -1)
 				g_usleep(1000);
-			
+
 			/* play the sound :) */
 			if (pdae_params->pplayback->playing && pdae_params->seektime == -1)
-				pdae_params->pplayback->pass_audio(pdae_params->pplayback, FMT_S16_LE, 2, 
+				pdae_params->pplayback->pass_audio(pdae_params->pplayback, FMT_S16_LE, 2,
 					bytecount, bytebuff, &pdae_params->pplayback->playing);
 			remainingbytes -= bytecount;
 			bytebuff += bytecount;
@@ -998,6 +807,273 @@
 	g_free(buffer);
 }
 
+static void scan_cd_threaded()
+{
+	if (scan_cd_thread != NULL) {
+		debug("A scan_cd thread is already running.\n");
+		return;
+	}
+
+	scan_cd_thread = g_thread_create((GThreadFunc)scan_cd, NULL, TRUE, NULL);
+	if (scan_cd_thread == NULL) {
+		cdaudio_error("Failed to create the thread for retrieving song information.\n");
+		return;
+	}
+}
+
+
+static void *scan_cd(void *nothing)
+{
+	debug("scan_cd started\n");
+
+	gint trackno;
+
+	/* find an available, audio capable, cd drive  */
+	if (cdng_cfg.device != NULL && strlen(cdng_cfg.device) > 0) {
+		pcdio = cdio_open(cdng_cfg.device, DRIVER_UNKNOWN);
+		if (pcdio == NULL) {
+			cdaudio_error("Failed to open CD device \"%s\".\n", cdng_cfg.device);
+			scan_cd_thread = NULL;
+			return NULL;
+		}
+	}
+	else {
+		gchar **ppcd_drives = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false);
+		pcdio = NULL;
+		if (ppcd_drives != NULL && *ppcd_drives != NULL) { /* we have at least one audio capable cd drive */
+			pcdio = cdio_open(*ppcd_drives, DRIVER_UNKNOWN);
+			if (pcdio == NULL) {
+				cdaudio_error("Failed to open CD.\n");
+				cleanup_on_error();
+				scan_cd_thread = NULL;
+				return NULL;
+			}
+			debug("found cd drive \"%s\" with audio capable media\n", *ppcd_drives);
+		}
+		else {
+			cdaudio_error("Unable to find or access a CDDA capable drive.\n");
+			cleanup_on_error();
+			scan_cd_thread = NULL;
+			return NULL;
+		}
+		if (ppcd_drives != NULL && *ppcd_drives != NULL)
+			cdio_free_device_list(ppcd_drives);
+	}
+
+	/* limit read speed */
+	if (cdng_cfg.limitspeed > 0 && cdng_cfg.use_dae) {
+		debug("setting drive speed limit to %dx\n", cdng_cfg.limitspeed);
+		if (cdio_set_speed(pcdio, cdng_cfg.limitspeed) != DRIVER_OP_SUCCESS)
+			cdaudio_error("Failed to set drive speed to %dx.\n", cdng_cfg.limitspeed);
+	}
+
+	/* general track initialization */
+	cdrom_drive_t *pcdrom_drive = cdio_cddap_identify_cdio(pcdio, 1, NULL);	// todo : check return / NULL
+	firsttrackno = cdio_get_first_track_num(pcdrom_drive->p_cdio);
+	lasttrackno = cdio_get_last_track_num(pcdrom_drive->p_cdio);
+	if (firsttrackno == CDIO_INVALID_TRACK || lasttrackno == CDIO_INVALID_TRACK) {
+		cdaudio_error("Failed to retrieve first/last track number.\n");
+		cleanup_on_error();
+		scan_cd_thread = NULL;
+		return NULL;
+	}
+	debug("first track is %d and last track is %d\n", firsttrackno, lasttrackno);
+
+	g_free(trackinfo);
+	trackinfo = (trackinfo_t *) g_new(trackinfo_t, (lasttrackno + 1));
+
+	cdaudio_set_fullinfo(&trackinfo[0],
+		cdio_get_track_lsn(pcdrom_drive->p_cdio, 0),
+		cdio_get_track_last_lsn(pcdrom_drive->p_cdio, CDIO_CDROM_LEADOUT_TRACK),
+		"", "", "");
+
+	for (trackno = firsttrackno; trackno <= lasttrackno; trackno++) {
+		cdaudio_set_fullinfo(&trackinfo[trackno],
+			cdio_get_track_lsn(pcdrom_drive->p_cdio, trackno),
+			cdio_get_track_last_lsn(pcdrom_drive->p_cdio, trackno),
+			"", "", "");
+
+		if (trackinfo[trackno].startlsn == CDIO_INVALID_LSN || trackinfo[trackno].endlsn == CDIO_INVALID_LSN) {
+			cdaudio_error("Failed to retrieve stard/end lsn for track %d.\n", trackno);
+			cleanup_on_error();
+			scan_cd_thread = NULL;
+			return NULL;
+		}
+	}
+
+	/* get trackinfo[0] cdtext information (the disc) */
+	if (cdng_cfg.use_cdtext) {
+		debug("getting cd-text information for disc\n");
+		cdtext_t *pcdtext = cdio_get_cdtext(pcdrom_drive->p_cdio, 0);
+		if (pcdtext == NULL || pcdtext->field[CDTEXT_TITLE] == NULL) {
+			debug("no cd-text available for disc\n");
+		}
+		else {
+			cdaudio_set_strinfo(&trackinfo[0],
+				pcdtext->field[CDTEXT_PERFORMER] ? pcdtext->field[CDTEXT_PERFORMER] : "",
+				pcdtext->field[CDTEXT_TITLE] ? pcdtext->field[CDTEXT_TITLE] : "",
+				pcdtext->field[CDTEXT_GENRE] ? pcdtext->field[CDTEXT_GENRE] : "");
+		}
+	}
+
+	/* get track information from cdtext */
+	gboolean cdtext_was_available = FALSE;
+	for (trackno = firsttrackno; trackno <= lasttrackno; trackno++) {
+		cdtext_t *pcdtext = NULL;
+		if (cdng_cfg.use_cdtext) {
+			debug("getting cd-text information for track %d\n", trackno);
+			pcdtext = cdio_get_cdtext(pcdrom_drive->p_cdio, trackno);
+			if (pcdtext == NULL || pcdtext->field[CDTEXT_PERFORMER] == NULL) {
+				debug("no cd-text available for track %d\n", trackno);
+				pcdtext = NULL;
+			}
+		}
+
+		if (pcdtext != NULL) {
+			cdaudio_set_strinfo(&trackinfo[trackno],
+				pcdtext->field[CDTEXT_PERFORMER] ? pcdtext->field[CDTEXT_PERFORMER] : "",
+				pcdtext->field[CDTEXT_TITLE] ? pcdtext->field[CDTEXT_TITLE] : "",
+				pcdtext->field[CDTEXT_GENRE] ? pcdtext->field[CDTEXT_GENRE] : "");
+			cdtext_was_available = TRUE;
+		}
+		else {
+			cdaudio_set_strinfo(&trackinfo[trackno], "", "", "");
+			g_snprintf(trackinfo[trackno].name, DEF_STRING_LEN, "CD Audio Track %02u", trackno);
+		}
+	}
+
+	if (!cdtext_was_available) {
+		/* initialize de cddb subsystem */
+		cddb_conn_t *pcddb_conn = NULL;
+		cddb_disc_t *pcddb_disc = NULL;
+		cddb_track_t *pcddb_track = NULL;
+
+		if (cdng_cfg.use_cddb) {
+			pcddb_conn = cddb_new();
+			if (pcddb_conn == NULL)
+				cdaudio_error("Failed to create the cddb connection.\n");
+			else {
+				debug("getting CDDB info\n");
+
+				if (cdng_cfg.use_proxy) {
+					cddb_http_proxy_enable(pcddb_conn);
+					cddb_set_http_proxy_server_name(pcddb_conn, cdng_cfg.proxy_host);
+					cddb_set_http_proxy_server_port(pcddb_conn, cdng_cfg.proxy_port);
+					cddb_set_http_proxy_username(pcddb_conn, cdng_cfg.proxy_username);
+					cddb_set_http_proxy_password(pcddb_conn, cdng_cfg.proxy_password);
+					cddb_set_server_name(pcddb_conn, cdng_cfg.cddb_server);
+					cddb_set_server_port(pcddb_conn, cdng_cfg.cddb_port);
+				}
+				else
+					if (cdng_cfg.cddb_http) {
+						cddb_http_enable(pcddb_conn);
+						cddb_set_server_name(pcddb_conn, cdng_cfg.cddb_server);
+						cddb_set_server_port(pcddb_conn, cdng_cfg.cddb_port);
+					}
+					else {
+						cddb_set_server_name(pcddb_conn, cdng_cfg.cddb_server);
+						cddb_set_server_port(pcddb_conn, cdng_cfg.cddb_port);
+					}
+
+				pcddb_disc = cddb_disc_new();
+				for (trackno = firsttrackno; trackno <= lasttrackno; trackno++) {
+					pcddb_track = cddb_track_new();
+					cddb_track_set_frame_offset(pcddb_track, trackinfo[trackno].startlsn);
+					cddb_disc_add_track(pcddb_disc, pcddb_track);
+				}
+
+				msf_t startmsf, endmsf;
+				cdio_get_track_msf(pcdio, 1, &startmsf);
+				cdio_get_track_msf(pcdio, CDIO_CDROM_LEADOUT_TRACK, &endmsf);
+				cddb_disc_set_length(pcddb_disc, cdio_audio_get_msf_seconds(&endmsf) - cdio_audio_get_msf_seconds(&startmsf));
+
+				cddb_disc_calc_discid(pcddb_disc);
+				int discid = cddb_disc_get_discid(pcddb_disc);
+				debug("CDDB disc id = %x\n", discid);
+
+				gint matches;
+				if ((matches = cddb_query(pcddb_conn, pcddb_disc)) == -1) {
+					if (cddb_errno(pcddb_conn) == CDDB_ERR_OK)
+						cdaudio_error("Failed to query the CDDB server\n");
+					else
+						cdaudio_error("Failed to query the CDDB server: %s\n", cddb_error_str(cddb_errno(pcddb_conn)));
+
+					cddb_disc_destroy(pcddb_disc);
+					pcddb_disc = NULL;
+				}
+				else {
+					if (matches == 0) {
+						debug("no cddb info available for this disc\n");
+
+						cddb_disc_destroy(pcddb_disc);
+						pcddb_disc = NULL;
+					}
+					else {
+						debug("CDDB disc category = \"%s\"\n", cddb_disc_get_category_str(pcddb_disc));
+
+						cddb_read(pcddb_conn, pcddb_disc);
+						if (cddb_errno(pcddb_conn) != CDDB_ERR_OK) {
+							cdaudio_error("failed to read the cddb info: %s\n", cddb_error_str(cddb_errno(pcddb_conn)));
+							cddb_disc_destroy(pcddb_disc);
+							pcddb_disc = NULL;
+						}
+						else {
+							debug("we have got the cddb info\n");
+							cdaudio_set_strinfo(&trackinfo[0],
+								cddb_disc_get_artist(pcddb_disc),
+								cddb_disc_get_title(pcddb_disc),
+								cddb_disc_get_genre(pcddb_disc));
+
+							int trackno;
+							for (trackno = firsttrackno; trackno <= lasttrackno; trackno++) {
+								cddb_track_t *pcddb_track = cddb_disc_get_track(pcddb_disc, trackno - 1);
+								cdaudio_set_strinfo(&trackinfo[trackno],
+									cddb_track_get_artist(pcddb_track),
+									cddb_track_get_title(pcddb_track),
+									cddb_disc_get_genre(pcddb_disc));
+							}
+						}
+					}
+				}
+			}
+		}
+
+		if (pcddb_disc != NULL)
+			cddb_disc_destroy(pcddb_disc);
+
+		if (pcddb_conn != NULL)
+			cddb_destroy(pcddb_conn);
+	}
+
+	if (cdng_cfg.debug) {
+		debug("disc has : performer = \"%s\", name = \"%s\", genre = \"%s\"\n",
+			trackinfo[0].performer, trackinfo[0].name, trackinfo[0].genre);
+
+		for (trackno = firsttrackno; trackno <= lasttrackno; trackno++) {
+			debug("track %d has : performer = \"%s\", name = \"%s\", genre = \"%s\", startlsn = %d, endlsn = %d\n",
+				trackno, trackinfo[trackno].performer, trackinfo[trackno].name, trackinfo[trackno].genre, trackinfo[trackno].startlsn, trackinfo[trackno].endlsn);
+		}
+	}
+
+	/*
+	if (pinputplayback != NULL) {
+		for (trackno = firsttrackno; trackno <= lasttrackno; trackno++) {
+			Tuple *tuple = create_tuple_from_trackinfo(trackno);
+			gchar *title = aud_tuple_formatter_make_title_string(tuple, aud_get_gentitle_format());
+
+			pinputplayback->set_params(pinputplayback, title, calculate_track_length(trackinfo[trackno].startlsn, trackinfo[trackno].endlsn), 1411200, 44100, 2);
+			g_free(title);
+			aud_tuple_free(tuple);
+		}
+	}
+	*/
+
+	debug("scan_cd ended\n");
+
+	scan_cd_thread = NULL;
+	return (void* ) -1;
+}
+
 static gint calculate_track_length(gint startlsn, gint endlsn)
 {
 	return ((endlsn - startlsn + 1) * 1000) / 75;
--- a/src/cdaudio-ng/configure.c	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/cdaudio-ng/configure.c	Sun Jan 20 14:53:47 2008 +0100
@@ -266,7 +266,7 @@
 {
 	configure_values_to_gui();
 	gtk_widget_show(configwindow);
-        gtk_window_present(GTK_WINDOW(configwindow));
+	gtk_window_present(GTK_WINDOW(configwindow));
 }
 
 
--- a/src/evdev-plug/ed_common.h	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/evdev-plug/ed_common.h	Sun Jan 20 14:53:47 2008 +0100
@@ -30,7 +30,7 @@
 
 #include "../../config.h"
 
-#define ED_VERSION_PLUGIN "0.2"
+#define ED_VERSION_PLUGIN "0.2p1"
 #define ED_VERSION_CONFIG "0"
 
 #define PLAYER_LOCALRC_FILE "evdev-plug.conf"
--- a/src/evdev-plug/ed_internals.c	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/evdev-plug/ed_internals.c	Sun Jan 20 14:53:47 2008 +0100
@@ -378,13 +378,13 @@
 
     /* parse content of /proc/bus/input/devices */
     regcomp( &preg,
-      "I:[^\n]*\nN: Name=\"([^\n]*)\"\nP: Phys=([^\n]*)\n[^\n]+\nH: Handlers=[^\n]*(event[0-9]+)[^\n]*\n" ,
+      "I:[^\n]*\nN: Name=\"([^\n]*)\"\nP: Phys=([^\n]*)\n([^\n]+\n)*H: Handlers=[^\n]*(event[0-9]+)[^\n]*\n" ,
       REG_ICASE | REG_EXTENDED );
 
     while ( search_offset > -1 )
     {
-      size_t nmatch = 4;
-      regmatch_t submatch[4];
+      size_t nmatch = 5;
+      regmatch_t submatch[5];
 
       if ( regexec( &preg , &buffer[search_offset] , nmatch , submatch , 0 ) == 0 )
       {
@@ -408,13 +408,13 @@
             submatch[2].rm_eo - submatch[2].rm_so );
         }
 
-        if ( submatch[3].rm_so != -1 ) /* check validity of filename sub-expression */
+        if ( submatch[4].rm_so != -1 ) /* check validity of filename sub-expression */
         {
           device_file = g_string_new( "" );
           GString *device_test = g_string_new( "" );
           g_string_append_len( device_file ,
-            &buffer[(search_offset + submatch[3].rm_so)] ,
-            submatch[3].rm_eo - submatch[3].rm_so );
+            &buffer[(search_offset + submatch[4].rm_so)] ,
+            submatch[4].rm_eo - submatch[4].rm_so );
 
           /* let's check if the filename actually exists in /dev */
           g_string_printf( device_test , "/dev/input/%s" , (char*)device_file->str );
--- a/src/neon/neon.c	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/neon/neon.c	Sun Jan 20 14:53:47 2008 +0100
@@ -484,6 +484,7 @@
 
     int ret;
     const ne_status* status;
+    ne_uri* rediruri;
 
     _ENTER;
 
@@ -501,9 +502,10 @@
     /*
      * Try to connect to the server.
      */
-    _DEBUG("Connecting...");
+    _DEBUG("<%p> Connecting...", handle);
     ret = ne_begin_request(handle->request);
     status = ne_get_status(handle->request);
+    _DEBUG("<%p> Return: %d, Status: %d", handle, ret, status->code);
     if ((NE_OK == ret) && (401 == status->code)) {
         /*
          * Authorization required. Reconnect to
@@ -514,10 +516,18 @@
         ret = ne_begin_request(handle->request);
     }
 
+    if ((NE_OK == ret) && ((301 == status->code) || (302 == status->code) || (303 == status->code) || (307 == status->code))) {
+        /*
+         * Redirect encountered. Reconnect.
+         */
+        ne_end_request(handle->request);
+        ret = NE_REDIRECT;
+    }
+
     switch (ret) {
         case NE_OK:
             /* URL opened OK */
-            _DEBUG("URL opened OK");
+            _DEBUG("<%p> URL opened OK", handle);
             handle->content_start = startbyte;
             handle->pos = startbyte;
             handle_headers(handle);
@@ -526,22 +536,25 @@
 
         case NE_REDIRECT:
             /* We hit a redirect. Handle it. */
-            _DEBUG("Redirect encountered");
+            _DEBUG("<%p> Redirect encountered", handle);
             handle->redircount += 1;
-            handle->purl = (ne_uri*)ne_redirect_location(handle->session);
+            rediruri = (ne_uri*)ne_redirect_location(handle->session);
             ne_request_destroy(handle->request);
-            if (NULL == handle->purl) {
-                _ERROR("Could not parse redirect response");
+
+            if (NULL == rediruri) {
+                _ERROR("<%p> Could not parse redirect response", handle);
                 _LEAVE -1;
             }
+            ne_uri_free(handle->purl);
+            ne_uri_copy(handle->purl, rediruri);
             _LEAVE 1;
             break;
 
         default:
             /* Something went wrong. */
-            _ERROR("Could not open URL: %d", ret);
+            _ERROR("<%p> Could not open URL: %d", ret);
             if (1 == ret) {
-                _ERROR("neon error string: %s", ne_get_error(handle->session));
+                _ERROR("<%p> neon error string: %s", ne_get_error(handle->session));
             }
             ne_request_destroy(handle->request);
             _LEAVE -1;
@@ -598,14 +611,15 @@
         _LEAVE -1;
     }
 
-    if (0 == handle->purl->port) {
-        handle->purl->port = 80;
-    }
-
     while (handle->redircount < 10) {
 
-        _DEBUG("<%p> Creating session", handle);
+        if (0 == handle->purl->port) {
+            handle->purl->port = ne_uri_defaultport(handle->purl->scheme);
+        }
+
+        _DEBUG("<%p> Creating session to %s://%s:%d", handle, handle->purl->scheme, handle->purl->host, handle->purl->port);
         handle->session = ne_session_create(handle->purl->scheme, handle->purl->host, handle->purl->port);
+        ne_redirect_register(handle->session);
         ne_add_server_auth(handle->session, NE_AUTH_BASIC, server_auth_callback, (void *)handle);
         ne_set_session_flag(handle->session, NE_SESSFLAG_ICYPROTO, 1);
         ne_set_session_flag(handle->session, NE_SESSFLAG_PERSIST, 0);
@@ -615,7 +629,6 @@
 
         ne_set_read_timeout(handle->session, 10);
         ne_set_useragent(handle->session, "Audacious/1.4.0");
-        ne_redirect_register(handle->session);
 
         if (use_proxy) {
             _DEBUG("<%p> Using proxy: %s:%d", handle, proxy_host, proxy_port);
@@ -631,6 +644,9 @@
             ne_session_destroy(handle->session);
             _LEAVE -1;
         }
+
+        _DEBUG("<%p> Following redirect...", handle);
+        ne_session_destroy(handle->session);
     }
 
     /*
--- a/src/paranormal/libcalc/parser.c	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/paranormal/libcalc/parser.c	Sun Jan 20 14:53:47 2008 +0100
@@ -489,8 +489,10 @@
 /* YYLEX -- calling `yylex' with the right arguments.  */
 
 #ifdef YYLEX_PARAM
+int yylex (YYSTYPE *yylval, void *yyparam);
 # define YYLEX yylex (&yylval, YYLEX_PARAM)
 #else
+int yylex (YYSTYPE *yylval);
 # define YYLEX yylex (&yylval)
 #endif
 
@@ -751,6 +753,7 @@
 #endif
 #endif /* ! YYPARSE_PARAM */
 
+int yyerror (char *s); 
 
 
 
@@ -1392,7 +1395,7 @@
       old_locale = setlocale (LC_ALL, NULL);
       saved_locale = g_strdup (old_locale);
       setlocale (LC_ALL, "C");
-      sscanf (((VFSBuffer *)(pc->input->handle))->iter, "%lf", &yylval->d_value);
+      sscanf ((const char*)((VFSBuffer *)(pc->input->handle))->iter, "%lf", &yylval->d_value);
 
       while (isdigit(c) || c == '.')
       {
--- a/src/scrobbler/configure.c	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/scrobbler/configure.c	Sun Jan 20 14:53:47 2008 +0100
@@ -21,8 +21,11 @@
 #include <gtk/gtk.h>
 
 #include "configure.h"
+#include "plugin.h"
 
 GtkWidget *entry1, *entry2, *ge_entry1, *ge_entry2, *cfgdlg;
+guint apply_timeout = 0; /* ID of timeout to save new config */
+gboolean running = TRUE; /* if plugin threads are running */
 
 static char *hexify(char *pass, int len)
 {
@@ -41,43 +44,79 @@
         return buf;
 }
 
-static void saveconfig(GtkWidget *wid __attribute__((unused)), gpointer data)
+static void saveconfig(void)
 {
-        ConfigDb *cfgfile;
+    ConfigDb *cfgfile;
+
+    const char *uid = gtk_entry_get_text(GTK_ENTRY(entry1));
+    const char *pwd = gtk_entry_get_text(GTK_ENTRY(entry2));
+    const char *ge_uid = gtk_entry_get_text(GTK_ENTRY(ge_entry1));
+    const char *ge_pwd = gtk_entry_get_text(GTK_ENTRY(ge_entry2));
+
+    if ((cfgfile = aud_cfg_db_open())) {
+        md5_state_t md5state;
+        unsigned char md5pword[16], ge_md5pword[16];
 
-        const char *uid = gtk_entry_get_text(GTK_ENTRY(entry1));
-        const char *pwd = gtk_entry_get_text(GTK_ENTRY(entry2));
-        const char *ge_uid = gtk_entry_get_text(GTK_ENTRY(ge_entry1));
-        const char *ge_pwd = gtk_entry_get_text(GTK_ENTRY(ge_entry2));
-
-        if ((cfgfile = aud_cfg_db_open()))
-	{
-                md5_state_t md5state;
-                unsigned char md5pword[16], ge_md5pword[16];
-
-                aud_cfg_db_set_string(cfgfile, "audioscrobbler", "username", (char *)uid);
-                aud_cfg_db_set_string(cfgfile, "audioscrobbler", "ge_username", (char *)ge_uid);
+        if (uid != NULL && uid[0] != '\0' && strlen(uid) &&
+            pwd != NULL && pwd[0] != '\0' && strlen(pwd))
+        {
+            aud_cfg_db_set_string(cfgfile, "audioscrobbler", "username", (char *)uid);
+            md5_init(&md5state);
+            md5_append(&md5state, (unsigned const char *)pwd, strlen(pwd));
+            md5_finish(&md5state, md5pword);
+            aud_cfg_db_set_string(cfgfile, "audioscrobbler", "password",
+                                 hexify((char*)md5pword, sizeof(md5pword)));
+        } else if (!uid || uid[0] == '\0') {
+            aud_cfg_db_set_string(cfgfile, "audioscrobbler", "username", "");
+            aud_cfg_db_set_string(cfgfile, "audioscrobbler", "password", "");
+        }
 
-                if (pwd != NULL && pwd[0] != '\0' && strlen(pwd))
-		{
-                        md5_init(&md5state);
-                        md5_append(&md5state, (unsigned const char *)pwd, strlen(pwd));
-                        md5_finish(&md5state, md5pword);
-                        aud_cfg_db_set_string(cfgfile, "audioscrobbler", "password",
-                                        hexify((char*)md5pword, sizeof(md5pword)));
-                }
+        if (ge_uid != NULL && ge_uid[0] != '\0' && strlen(ge_uid) &&
+            ge_pwd != NULL && ge_pwd[0] != '\0' && strlen(ge_pwd))
+        {
+            aud_cfg_db_set_string(cfgfile, "audioscrobbler", "ge_username", (char *)ge_uid);
+            md5_init(&md5state);
+            md5_append(&md5state, (unsigned const char *)ge_pwd, strlen(ge_pwd));
+            md5_finish(&md5state, ge_md5pword);
+            aud_cfg_db_set_string(cfgfile, "audioscrobbler", "ge_password",
+                                  hexify((char*)ge_md5pword, sizeof(ge_md5pword)));
+        } else if (!ge_uid || ge_uid[0] == '\0') {
+            aud_cfg_db_set_string(cfgfile, "audioscrobbler", "ge_username", "");
+            aud_cfg_db_set_string(cfgfile, "audioscrobbler", "ge_password", "");
+        }
+
+        aud_cfg_db_close(cfgfile);
+    }
+}
 
-                if (ge_pwd != NULL && ge_pwd[0] != '\0' && strlen(ge_pwd))
-		{
-                        md5_init(&md5state);
-                        md5_append(&md5state, (unsigned const char *)ge_pwd, strlen(ge_pwd));
-                        md5_finish(&md5state, ge_md5pword);
-                        aud_cfg_db_set_string(cfgfile, "audioscrobbler", "ge_password",
-                                        hexify((char*)ge_md5pword, sizeof(ge_md5pword)));
-                }
+static gboolean apply_config_changes(gpointer data) {
+    apply_timeout = 0;
+    saveconfig();
+    start();
+    running = TRUE;
+    return FALSE;
+}
 
-                aud_cfg_db_close(cfgfile);
-        }
+void configure_cleanup(void) {
+    if (apply_timeout) { /* config has been changed, but wasn't saved yet */
+        g_source_remove(apply_timeout);
+        apply_timeout = 0;
+        saveconfig();
+    }
+}
+
+static void
+entry_changed(GtkWidget *widget, gpointer data)
+{
+    if (running) {
+        stop();
+        running = FALSE;
+    }
+
+    if (apply_timeout)
+        g_source_remove(apply_timeout);
+
+    apply_timeout = g_timeout_add_seconds(10, (GSourceFunc) apply_config_changes, NULL);
 }
 
 /* Generated by glade, sorta. */
@@ -140,7 +179,6 @@
   gtk_entry_set_visibility(GTK_ENTRY(entry2), FALSE);
   gtk_widget_show (entry2);
   gtk_table_attach_defaults (GTK_TABLE (table1), entry2, 1, 2, 3, 4);
-  g_signal_connect(entry2, "changed", (GCallback) saveconfig, NULL);
 
   label1 = gtk_label_new (_("<b>Last.FM</b>"));
   gtk_label_set_use_markup (GTK_LABEL (label1), TRUE);
@@ -180,7 +218,6 @@
   gtk_entry_set_visibility(GTK_ENTRY(ge_entry2), FALSE);
   gtk_widget_show (ge_entry2);
   gtk_table_attach_defaults (GTK_TABLE (table1), ge_entry2, 1, 2, 3, 4);
-  g_signal_connect(ge_entry2, "changed", (GCallback) saveconfig, NULL);
 
   label1 = gtk_label_new (_("<b>Gerpok</b>"));
   gtk_label_set_use_markup (GTK_LABEL (label1), TRUE);
@@ -219,6 +256,11 @@
                 aud_cfg_db_close(db);
         }
 
+  g_signal_connect(entry1, "changed", G_CALLBACK(entry_changed), NULL);
+  g_signal_connect(entry2, "changed", G_CALLBACK(entry_changed), NULL);
+  g_signal_connect(ge_entry1, "changed", G_CALLBACK(entry_changed), NULL);
+  g_signal_connect(ge_entry2, "changed", G_CALLBACK(entry_changed), NULL);
+
   return vbox2;
 }
 
--- a/src/scrobbler/configure.h	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/scrobbler/configure.h	Sun Jan 20 14:53:47 2008 +0100
@@ -2,5 +2,6 @@
 #define _CONFIGURE_H_
 
 GtkWidget* create_cfgdlg (void);
+void configure_cleanup(void);
 
 #endif
--- a/src/scrobbler/plugin.c	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/scrobbler/plugin.c	Sun Jan 20 14:53:47 2008 +0100
@@ -23,6 +23,7 @@
 #include <wchar.h>
 #include <sys/time.h>
 
+#include "plugin.h"
 #include "scrobbler.h"
 #include "gerpok.h"
 #include "gtkstuff.h"
@@ -30,7 +31,6 @@
 #include "fmt.h"
 #include "configure.h"
 
-#define XS_CS xmms_scrobbler.xmms_session
 #define XS_SLEEP 1
 #define HS_SLEEP 10
 
@@ -100,17 +100,13 @@
     }
 }
 
-static void init(void)
-{
+void start(void) {
 	char *username = NULL, *password = NULL;
 	char *ge_username = NULL, *ge_password = NULL;
 	ConfigDb *cfgfile;
 	sc_going = 1;
 	ge_going = 1;
 	GError **moo = NULL;
-	cfgdlg = create_cfgdlg();
-
-        aud_prefswin_page_new(cfgdlg, "Scrobbler", DATA_DIR "/images/audioscrobbler.png");
 
 	if ((cfgfile = aud_cfg_db_open()) != NULL) {
 		aud_cfg_db_get_string(cfgfile, "audioscrobbler", "username",
@@ -180,10 +176,7 @@
 	pdebug("plugin started", DEBUG);
 }
 
-static void cleanup(void)
-{
-        aud_prefswin_page_destroy(cfgdlg);
-
+void stop(void) {
 	if (!sc_going && !ge_going)
 		return;
 	pdebug("about to lock mutex", DEBUG);
@@ -219,6 +212,20 @@
 	aud_hook_dissociate("playback end", aud_hook_playback_end);
 }
 
+static void init(void)
+{
+    start();
+    cfgdlg = create_cfgdlg();
+    aud_prefswin_page_new(cfgdlg, "Scrobbler", DATA_DIR "/images/audioscrobbler.png");
+}
+
+static void cleanup(void)
+{
+    stop();
+    configure_cleanup();
+    aud_prefswin_page_destroy(cfgdlg);
+}
+
 static void *xs_thread(void *data __attribute__((unused)))
 {
 	int run = 1;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/scrobbler/plugin.h	Sun Jan 20 14:53:47 2008 +0100
@@ -0,0 +1,7 @@
+#ifndef PLUGIN_H
+#define PLUGIN_H
+
+void start(void);
+void stop(void);
+
+#endif
--- a/src/scrobbler/scrobbler.c	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/scrobbler/scrobbler.c	Sun Jan 20 14:53:47 2008 +0100
@@ -20,7 +20,7 @@
 #define SCROBBLER_HS_WAIT 1800
 #define SCROBBLER_SB_WAIT 10
 #define SCROBBLER_VERSION "1.2"
-#define SCROBBLER_IMPLEMENTATION "0.1"		/* This is the implementation, not the player version. */
+#define SCROBBLER_IMPLEMENTATION "0.2"		/* This is the implementation, not the player version. */
 #define SCROBBLER_SB_MAXLEN 1024
 #define CACHE_SIZE 1024
 
--- a/src/timidity/libtimidity/playmidi.c	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/timidity/libtimidity/playmidi.c	Sun Jan 20 14:53:47 2008 +0100
@@ -774,9 +774,9 @@
       song->current_event++;
     }
     if (song->current_event->time > end_sample)
-      compute_data(song, (sint8 **)&ptr, end_sample-song->current_sample);
+      compute_data(song, (sint8 **)(void*)&ptr, end_sample-song->current_sample);
     else
-      compute_data(song, (sint8 **)&ptr, song->current_event->time-song->current_sample);
+      compute_data(song, (sint8 **)(void*)&ptr, song->current_event->time-song->current_sample);
   }
   return samples * song->bytes_per_sample;
 }
--- a/src/tta/ttadec.c	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/tta/ttadec.c	Sun Jan 20 14:53:47 2008 +0100
@@ -472,7 +472,7 @@
 }
 
 int get_samples (byte *buffer) {
-	unsigned int k, depth, unary, binary;
+	unsigned int k, depth, unary, binary=0;
 	byte *p = buffer;
 	decoder *dec = tta;
 	int *prev = cache;
--- a/src/vorbis/vorbis.c	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/vorbis/vorbis.c	Sun Jan 20 14:53:47 2008 +0100
@@ -2,6 +2,7 @@
  * Copyright (C) Tony Arcieri <bascule@inferno.tusculum.edu>
  * Copyright (C) 2001-2002  Haavard Kvaalen <havardk@xmms.org>
  * Copyright (C) 2007 William Pitcock <nenolod@sacredspiral.co.uk>
+ * Copyright (C) 2008 Cristi Măgherușan <majeru@gentoo.ro>
  *
  * ReplayGain processing Copyright (C) 2002 Gian-Carlo Pascutto <gcp@sjeng.org>
  *
@@ -230,95 +231,6 @@
     }
 }
 
-static int
-vorbis_process_data(InputPlayback *playback, int last_section, 
-		    gboolean use_rg, float rg_scale)
-{
-    char pcmout[4096];
-    int bytes;
-    float **pcm;
-
-    /*
-     * A vorbis physical bitstream may consist of many logical
-     * sections (information for each of which may be fetched from
-     * the vf structure).  This value is filled in by ov_read to
-     * alert us what section we're currently decoding in case we
-     * need to change playback settings at a section boundary
-     */
-    int current_section = last_section;
-
-    g_mutex_lock(vf_mutex);
-    if (use_rg) {
-        bytes =
-            ov_read_float(&vf, &pcm, sizeof(pcmout) / 2 / channels,
-                          &current_section);
-        if (bytes > 0)
-            bytes = vorbis_process_replaygain(pcm, bytes, channels,
-                                              pcmout, rg_scale);
-    }
-    else {
-        bytes = ov_read(&vf, pcmout, sizeof(pcmout),
-                        (int) (G_BYTE_ORDER == G_BIG_ENDIAN),
-                        2, 1, &current_section);
-    }
-
-    /*
-     * We got some sort of error. Bail.
-     */
-    if (bytes <= 0 && bytes != OV_HOLE) {
-        g_mutex_unlock(vf_mutex);
-        playback->playing = 0;
-        playback->output->buffer_free();
-        playback->output->buffer_free();
-        playback->eof = TRUE;
-        return last_section;
-    }
-
-    if (current_section != last_section) {
-        /*
-         * The info struct is different in each section.  vf
-         * holds them all for the given bitstream.  This
-         * requests the current one
-         */
-        vorbis_info *vi = ov_info(&vf, -1);
-
-        if (vi->channels > 2) {
-            playback->eof = TRUE;
-            g_mutex_unlock(vf_mutex);
-            return current_section;
-        }
-
-
-        if (vi->rate != samplerate || vi->channels != channels) {
-            samplerate = vi->rate;
-            channels = vi->channels;
-            playback->output->buffer_free();
-            playback->output->buffer_free();
-            playback->output->close_audio();
-            if (!playback->output->
-                open_audio(FMT_S16_NE, vi->rate, vi->channels)) {
-                playback->error = TRUE;
-                playback->eof = TRUE;
-                g_mutex_unlock(vf_mutex);
-                return current_section;
-            }
-            playback->output->flush(ov_time_tell(&vf) * 1000);
-        }
-    }
-
-    g_mutex_unlock(vf_mutex);
-
-    playback->pass_audio(playback, FMT_S16_NE, channels, bytes, pcmout, &playback->playing);
-
-    if (!playback->playing)
-        return current_section;
-
-    if (seekneeded != -1)
-        do_seek(playback);
-
-    return current_section;
-}
-
 static gpointer
 vorbis_play_loop(gpointer arg)
 {
@@ -350,12 +262,23 @@
     fd->fd = stream;
     datasource = (void *) fd;
 
+    char pcmout[4096];
+    int bytes;
+    float **pcm;
+
     /*
      * The open function performs full stream detection and
      * machine initialization.  None of the rest of ov_xx() works
      * without it
+     *
+     * A vorbis physical bitstream may consist of many logical
+     * sections (information for each of which may be fetched from
+     * the vf structure).  This value is filled in by ov_read to
+     * alert us what section we're currently decoding in case we
+     * need to change playback settings at a section boundary
      */
-
+ 
+   
     g_mutex_lock(vf_mutex);
     if (ov_open_callbacks(datasource, &vf, NULL, 0, aud_vfs_is_streaming(fd->fd) ? vorbis_callbacks_stream : vorbis_callbacks) < 0) {
         vorbis_callbacks.close_func(datasource);
@@ -403,57 +326,146 @@
      */
 
     while (playback->playing) {
-        int current_section;
-
-        if (seekneeded != -1)
-            do_seek(playback);
-
+       
         if (playback->eof) {
             g_usleep(20000);
             continue;
         }
 
-        current_section = vorbis_process_data(playback, last_section, 
-					      use_rg, rg_scale);
+        if (seekneeded != -1)
+            do_seek(playback);
+
+        
+        int current_section = last_section;
 
-        if (current_section != last_section) {
+        g_mutex_lock(vf_mutex);
+        if (use_rg) {
+            bytes =
+                ov_read_float(&vf, &pcm, sizeof(pcmout) / 2 / channels,
+                        &current_section);
+            if (bytes > 0)
+                bytes = vorbis_process_replaygain(pcm, bytes, channels,
+                        pcmout, rg_scale);
+        }
+        else {
+            bytes = ov_read(&vf, pcmout, sizeof(pcmout),
+                    (int) (G_BYTE_ORDER == G_BIG_ENDIAN),
+                    2, 1, &current_section);
+        }
+
+        /*
+         * We got some sort of error. Bail.
+         */
+        if (bytes <= 0 && bytes != OV_HOLE) {
+           /*
+            * EOF
+            */
+            playback->playing = 0;
+            playback->output->buffer_free();
+            playback->output->buffer_free();
+            playback->eof = TRUE;
+            current_section = last_section;
+        }
+
+        
+
+        if (current_section <= last_section) {
             /*
-             * set total play time, bitrate, rate, and channels of
-             * current section
+             * The info struct is different in each section.  vf
+             * holds them all for the given bitstream.  This
+             * requests the current one
              */
-            if (title)
-                g_free(title);
+            vorbis_info *vi = ov_info(&vf, -1);
+
+            if (vi->channels > 2) {
+                playback->eof = TRUE;
+                g_mutex_unlock(vf_mutex);
+                goto stop_processing;
+            }
+
 
-            g_mutex_lock(vf_mutex);
-            title = vorbis_generate_title(&vf, filename);
-            use_rg = vorbis_update_replaygain(&rg_scale);
+            if (vi->rate != samplerate || vi->channels != channels) {
+                samplerate = vi->rate;
+                channels = vi->channels;
+                playback->output->buffer_free();
+                playback->output->buffer_free();
+                playback->output->close_audio();
+                if (!playback->output->
+                        open_audio(FMT_S16_NE, vi->rate, vi->channels)) {
+                    playback->error = TRUE;
+                    playback->eof = TRUE;
+                    g_mutex_unlock(vf_mutex);
+                    goto stop_processing;
+                }
+                playback->output->flush(ov_time_tell(&vf) * 1000);
+            }
+        }
 
-            if (time != -1)
-                time = ov_time_total(&vf, -1) * 1000;
+        g_mutex_unlock(vf_mutex);
+
+        playback->pass_audio(playback, FMT_S16_NE, channels, bytes, pcmout, &playback->playing);
 
-            g_mutex_unlock(vf_mutex);
+        if (!playback->playing)
+            goto stop_processing;
+
+        if (seekneeded != -1)
+            do_seek(playback);
 
-            playback->set_params(playback, title, time, br, samplerate, channels);
-            timercount = playback->output->output_time();
+        stop_processing:
+   
+        if (current_section <= last_section) {
+        /*
+         * set total play time, bitrate, rate, and channels of
+         * current section
+         */
+        if (title)
+            g_free(title);
+
+        g_mutex_lock(vf_mutex);
+        title = vorbis_generate_title(&vf, filename);
+        use_rg = vorbis_update_replaygain(&rg_scale);
 
-            last_section = current_section;
+        if (time != -1)
+            time = ov_time_total(&vf, -1) * 1000;
+
+        g_mutex_unlock(vf_mutex);
+   
+        playback->set_params(playback, title, time, br, samplerate, channels);
+
+        timercount = playback->output->output_time();
+
+        last_section = current_section;
+         
         }
-    }
-    if (!playback->error)
-        playback->output->close_audio();
+}
+
+
+
+if (!playback->error)
+    playback->output->close_audio();
     /* fall through intentional */
 
-  play_cleanup:
-    g_free(title);
+    /*this loop makes it not skip the last ~4 seconds, but the playback 
+     * timer isn't updated in this period, so it still needs a bit of work 
+     *
+     * majeru
+     */
+while(playback->output->buffer_playing()&& playback->output->buffer_free())
+    g_usleep(50000);
 
-    /*
-     * ov_clear closes the stream if its open.  Safe to call on an
-     * uninitialized structure as long as we've zeroed it
-     */
+
+play_cleanup:
+g_free(title);
+
+/*
+ * ov_clear closes the stream if its open.  Safe to call on an
+ * uninitialized structure as long as we've zeroed it
+ */
     g_mutex_lock(vf_mutex);
     ov_clear(&vf);
     g_mutex_unlock(vf_mutex);
     playback->playing = 0;
+    playback->output->buffer_free();
     return NULL;
 }
 
@@ -886,7 +898,7 @@
     if (handle->probe == FALSE)
     {
         ret = aud_vfs_fclose(handle->fd);
-//        g_free(handle); // it causes double free. i'm not really sure that commenting out at here is correct. --yaz
+/*        g_free(handle);  it causes double free. i'm not really sure that commenting out at here is correct. --yaz*/
     }
 
     return ret;
--- a/src/wma/libffwma/fft.c	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/wma/libffwma/fft.c	Sun Jan 20 14:53:47 2008 +0100
@@ -301,9 +301,9 @@
     }
     return 0;
  fail:
-    av_freep(&s->revtab);
-    av_freep(&s->exptab);
-    av_freep(&s->exptab1);
+    av_freep((void*)&s->revtab);
+    av_freep((void*)&s->exptab);
+    av_freep((void*)&s->exptab1);
     return -1;
 }
 
@@ -431,8 +431,8 @@
 
 void fft_end(FFTContext *s)
 {
-    av_freep(&s->revtab);
-    av_freep(&s->exptab);
-    av_freep(&s->exptab1);
+    av_freep((void*)&s->revtab);
+    av_freep((void*)&s->exptab);
+    av_freep((void*)&s->exptab1);
 }
 
--- a/src/wma/libffwma/mdct.c	Sun Jan 20 14:52:44 2008 +0100
+++ b/src/wma/libffwma/mdct.c	Sun Jan 20 14:53:47 2008 +0100
@@ -52,8 +52,8 @@
         goto fail;
     return 0;
  fail:
-    av_freep(&s->tcos);
-    av_freep(&s->tsin);
+    av_freep((void*)&s->tcos);
+    av_freep((void*)&s->tsin);
     return -1;
 }
 
@@ -169,7 +169,7 @@
 
 void ff_mdct_end(MDCTContext *s)
 {
-    av_freep(&s->tcos);
-    av_freep(&s->tsin);
+    av_freep((void*)&s->tcos);
+    av_freep((void*)&s->tsin);
     fft_end(&s->fft);
 }