changeset 1453:572db023e7a6

merge
author Cristi Magherusan <majeru@atheme-project.org>
date Fri, 10 Aug 2007 14:23:20 +0300
parents d43075afd1db (current diff) dc3e28d3b92a (diff)
children f57f3af1f722
files
diffstat 30 files changed, 582 insertions(+), 538 deletions(-) [+]
line wrap: on
line diff
--- a/src/aac/src/libmp4.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/aac/src/libmp4.c	Fri Aug 10 14:23:20 2007 +0300
@@ -6,13 +6,15 @@
 #include "mp4ff.h"
 #include "tagging.h"
 
-#include <audacious/plugin.h>
-#include <audacious/output.h>
-#include <audacious/util.h>
-#include <audacious/titlestring.h>
-#include <audacious/vfs.h>
-#include <audacious/i18n.h>
-#include <audacious/strings.h>
+#include "audacious/plugin.h"
+#include "audacious/output.h"
+#include "audacious/util.h"
+#include "audacious/vfs.h"
+#include "audacious/i18n.h"
+#include "audacious/strings.h"
+#include "audacious/main.h"
+#include "audacious/tuple.h"
+#include "audacious/tuple_formatter.h"
 
 #define MP4_VERSION VERSION
 
@@ -41,7 +43,7 @@
 static void        mp4_seek(InputPlayback *, int);
 static void        mp4_cleanup(void);
 static void        mp4_get_song_title_len(char *filename, char **, int *);
-static TitleInput* mp4_get_song_tuple(char *);
+static Tuple*      mp4_get_song_tuple(char *);
 static int         mp4_is_our_fd(char *, VFSFile *);
 
 static gchar *fmts[] = { "m4a", "mp4", "aac", NULL };
@@ -103,17 +105,6 @@
     return vfs_fseek((VFSFile *) data, pos, SEEK_SET);
 }
 
-static gchar *
-extname(const char *filename)
-{
-    gchar *ext = strrchr(filename, '.');
-
-    if (ext != NULL)
-        ++ext;
-
-    return ext;
-}
-
 static void mp4_init(void)
 {
     mp4cfg.file_type = FILE_UNKNOWN;
@@ -326,36 +317,25 @@
 {
 }
 
-static TitleInput *mp4_get_song_tuple(char *fn)
+static Tuple *mp4_get_song_tuple_base(char *filename, VFSFile *mp4fh)
 {
     mp4ff_callback_t *mp4cb = g_malloc0(sizeof(mp4ff_callback_t));
-    VFSFile *mp4fh;
     mp4ff_t *mp4file;
-    TitleInput *input = NULL;
-    gchar *filename = g_strdup(fn);
-    gboolean remote = str_has_prefix_nocase(filename, "http:") ||
-	              str_has_prefix_nocase(filename, "https:");
-
-    mp4fh = remote ? vfs_buffered_file_new_from_uri(filename) : vfs_fopen(filename, "rb");
+    Tuple *ti = tuple_new_from_filename(filename);
 
     /* check if this file is an ADTS stream, if so return a blank tuple */
     if (parse_aac_stream(mp4fh))
     {
         g_free(mp4cb);
 
-        input = bmp_title_input_new();
-
-        input->track_name = vfs_get_metadata(mp4fh, "track-name");
-        input->album_name = vfs_get_metadata(mp4fh, "stream-name");
+        tuple_associate_string(ti, "title", vfs_get_metadata(mp4fh, "track-name"));
+        tuple_associate_string(ti, "album", vfs_get_metadata(mp4fh, "stream-name"));
 
-        input->file_name = g_path_get_basename(filename);
-        input->file_path = g_path_get_dirname(filename);
-        input->file_ext = extname(filename);
+        tuple_associate_string(ti, "codec", "Advanced Audio Coding (AAC)");
+        tuple_associate_string(ti, "quality", "lossy");
 
-        input->mtime = 0;
-        input->length = -1;
-
-        return input;
+        vfs_fclose(mp4fh);
+        return ti;
     }
 
     vfs_rewind(mp4fh);
@@ -409,30 +389,63 @@
 
         msDuration = ((float)numSamples * (float)(framesize - 1.0)/(float)samplerate) * 1000;
 
-        input = bmp_title_input_new();
-
-        mp4ff_meta_get_title(mp4file, &input->track_name);
-        mp4ff_meta_get_album(mp4file, &input->album_name);
-        mp4ff_meta_get_artist(mp4file, &input->performer);
-        mp4ff_meta_get_date(mp4file, &tmpval);
-        mp4ff_meta_get_genre(mp4file, &input->genre);
-
+        mp4ff_meta_get_title(mp4file, &tmpval);
         if (tmpval)
         {
-            input->year = atoi(tmpval);
+            tuple_associate_string(ti, "title", tmpval);
+            free(tmpval);
+        }
+
+        mp4ff_meta_get_album(mp4file, &tmpval);
+        if (tmpval)
+        {
+            tuple_associate_string(ti, "title", tmpval);
             free(tmpval);
         }
 
-        input->file_name = g_path_get_basename(filename);
-        input->file_path = g_path_get_dirname(filename);
-        input->file_ext = extname(filename);
-        input->length = msDuration;
+        mp4ff_meta_get_artist(mp4file, &tmpval);
+        if (tmpval)
+        {
+            tuple_associate_string(ti, "artist", tmpval);
+            free(tmpval);
+        }
+
+        mp4ff_meta_get_genre(mp4file, &tmpval);
+        if (tmpval)
+        {
+            tuple_associate_string(ti, "genre", tmpval);
+            free(tmpval);
+        }
+
+        mp4ff_meta_get_date(mp4file, &tmpval);
+        if (tmpval)
+        {
+            tuple_associate_int(ti, "year", atoi(tmpval));
+            free(tmpval);
+        }
+
+        tuple_associate_string(ti, "codec", "Advanced Audio Coding (AAC)");
+        tuple_associate_string(ti, "quality", "lossy");
 
         free (mp4cb);
         vfs_fclose(mp4fh);
     }
 
-    return input;
+    return ti;
+}
+
+static Tuple *mp4_get_song_tuple(char *filename)
+{
+    Tuple *tuple;
+    VFSFile *mp4fh;
+    gboolean remote = str_has_prefix_nocase(filename, "http:") ||
+	              str_has_prefix_nocase(filename, "https:");
+
+    mp4fh = remote ? vfs_buffered_file_new_from_uri(filename) : vfs_fopen(filename, "rb");
+
+    tuple = mp4_get_song_tuple_base(filename, mp4fh);
+
+    return tuple;
 }
 
 static void mp4_get_song_title_len(char *filename, char **title, int *len)
@@ -443,61 +456,12 @@
 
 static gchar   *mp4_get_song_title(char *filename)
 {
-    mp4ff_callback_t *mp4cb = g_malloc0(sizeof(mp4ff_callback_t));
-    VFSFile *mp4fh;
-    mp4ff_t *mp4file;
-    gchar *title = NULL;
-
-    mp4fh = vfs_fopen(filename, "rb");
-    mp4cb->read = mp4_read_callback;
-    mp4cb->seek = mp4_seek_callback;
-    mp4cb->user_data = mp4fh;
-
-    if (!(mp4file = mp4ff_open_read(mp4cb))) {
-        g_free(mp4cb);
-        vfs_fclose(mp4fh);
-    } else {
-        TitleInput *input;
-        gchar *tmpval;
-
-        input = bmp_title_input_new();
-
-        mp4ff_meta_get_title(mp4file, &input->track_name);
-        mp4ff_meta_get_album(mp4file, &input->album_name);
-        mp4ff_meta_get_artist(mp4file, &input->performer);
-        mp4ff_meta_get_date(mp4file, &tmpval);
-        mp4ff_meta_get_genre(mp4file, &input->genre);
+    gchar *title;
+    Tuple *tuple = mp4_get_song_tuple(filename);
 
-        if (tmpval)
-        {
-            input->year = atoi(tmpval);
-            free(tmpval);
-        }
-
-        input->file_name = g_path_get_basename(filename);
-        input->file_path = g_path_get_dirname(filename);
-        input->file_ext = extname(filename);
-
-        title = xmms_get_titlestring(xmms_get_gentitle_format(), input);
+    title = tuple_formatter_process_string(tuple, cfg.gentitle_format);
 
-        free (input->track_name);
-        free (input->album_name);
-        free (input->performer);
-        free (input->genre);
-        free (input->file_name);
-        free (input->file_path);
-        free (input);
-
-        free (mp4cb);
-        vfs_fclose(mp4fh);
-    }
-
-    if (!title)
-    {
-        title = g_path_get_basename(filename);
-        if (extname(title))
-            *(extname(title) - 1) = '\0';
-    }
+    mowgli_object_unref(tuple);
 
     return title;
 }
--- a/src/aac/src/mp4_utils.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/aac/src/mp4_utils.c	Fri Aug 10 14:23:20 2007 +0300
@@ -10,7 +10,6 @@
 #include <string.h>
 #include <stdlib.h>
 #include "audacious/plugin.h"
-#include "audacious/titlestring.h"
 #include "audacious/util.h"
 
 const char *mp4AudioNames[]=
--- a/src/alac/plugin.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/alac/plugin.c	Fri Aug 10 14:23:20 2007 +0300
@@ -46,6 +46,7 @@
 #include <audacious/i18n.h>
 
 #include <audacious/plugin.h>
+#include <audacious/main.h>
 #include <audacious/output.h>
 #include <audacious/vfs.h>
 #include <audacious/util.h>
@@ -116,31 +117,30 @@
     return TRUE;
 }
 
-TitleInput *build_tuple_from_demux(demux_res_t *demux_res, char *path)
+Tuple *build_tuple_from_demux(demux_res_t *demux_res, char *path)
 {
-    TitleInput *ti = bmp_title_input_new();
+    Tuple *ti = tuple_new_from_filename(path);
 
     if (demux_res->tuple.art != NULL)
-        ti->performer = g_strdup(demux_res->tuple.art);
+        tuple_associate_string(ti, "artist", demux_res->tuple.art);
     if (demux_res->tuple.nam != NULL)
-        ti->track_name = g_strdup(demux_res->tuple.nam);
+        tuple_associate_string(ti, "title", demux_res->tuple.nam);
     if (demux_res->tuple.alb != NULL)
-        ti->album_name = g_strdup(demux_res->tuple.alb);
+        tuple_associate_string(ti, "album", demux_res->tuple.alb);
     if (demux_res->tuple.gen != NULL)
-        ti->genre = g_strdup(demux_res->tuple.gen);
+        tuple_associate_string(ti, "genre", demux_res->tuple.gen);
     if (demux_res->tuple.cmt != NULL)
-        ti->comment = g_strdup(demux_res->tuple.cmt);
+        tuple_associate_string(ti, "comment", demux_res->tuple.cmt);
     if (demux_res->tuple.day != NULL)
-        ti->year = atoi(demux_res->tuple.day);
+        tuple_associate_int(ti, "year", atoi(demux_res->tuple.day));
 
-    ti->file_name = g_path_get_basename(path);
-    ti->file_path = g_path_get_dirname(path);
-    ti->file_ext = extname(path);
+    tuple_associate_string(ti, "codec", "Apple Lossless (ALAC)");
+    tuple_associate_string(ti, "quality", "lossless");
 
     return ti;
 }
 
-TitleInput *build_tuple(char *filename)
+Tuple *build_tuple(char *filename)
 {
     demux_res_t demux_res;
     VFSFile *input_file;
@@ -313,7 +313,7 @@
     gulong duration = 0;	/* samples added up */
     VFSFile *input_file;
     stream_t *input_stream;
-    TitleInput *ti;
+    Tuple *ti;
     gchar *title;
 
     memset(&demux_res, 0, sizeof(demux_res));
@@ -335,7 +335,7 @@
 
     /* Get the titlestring ready. */
     ti = build_tuple_from_demux(&demux_res, (char *) args);
-    title = xmms_get_titlestring(xmms_get_gentitle_format(), ti);
+    title = tuple_formatter_process_string(ti, cfg.gentitle_format);
 
     /* initialise the sound converter */
     demux_res.alac = create_alac(demux_res.sample_size, demux_res.num_channels);
--- a/src/console/Audacious_Driver.cxx	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/console/Audacious_Driver.cxx	Fri Aug 10 14:23:20 2007 +0300
@@ -13,9 +13,11 @@
 #include <glib.h>
 #include <audacious/i18n.h>
 #include <gtk/gtk.h>
+extern "C" {
+#include "audacious/main.h"
+#include "audacious/tuple.h"
+#include "audacious/tuple_formatter.h"
 #include "audacious/util.h"
-#include "audacious/titlestring.h"
-extern "C" {
 #include "audacious/output.h"
 #include "audacious/playlist.h"
 }
@@ -200,21 +202,29 @@
 	return g_strdup(in);
 }
 
-static TitleInput* get_track_ti( const char* path, track_info_t const& info, int track )
+static Tuple* get_track_ti( const char* path, track_info_t const& info, int track )
 {
-	TitleInput* ti = bmp_title_input_new();
+	Tuple* ti = tuple_new();
 	if ( ti )
 	{
-		ti->file_name  = g_path_get_basename( path );
-		ti->file_path  = g_path_get_dirname ( path );
-		ti->performer  = selective_strdup( info.author );
-		ti->album_name = selective_strdup( info.game );
-		ti->track_name = selective_strdup( info.song ? info.song : ti->file_name );
+		tuple_associate_string(ti, "file-name", g_path_get_basename(path));
+		tuple_associate_string(ti, "file-path", g_path_get_dirname(path));
+		tuple_associate_string(ti, "artist", info.author);
+		tuple_associate_string(ti, "album", info.game);
+		tuple_associate_string(ti, "game", info.game);
+		tuple_associate_string(ti, "title", info.song ? info.song : g_path_get_basename(path));
 		if ( info.track_count > 1 )
-			ti->track_number = track + 1;
-		ti->comment    = selective_strdup( info.copyright );
-		ti->genre      = g_strconcat( "Console: ", info.system, NULL );
-		
+		{
+			tuple_associate_int(ti, "track-number", track + 1);
+			tuple_associate_int(ti, "subsong", track);
+		}
+		tuple_associate_string(ti, "copyright", info.copyright);
+		tuple_associate_string(ti, "console", info.system);
+		tuple_associate_string(ti, "codec", info.system);
+		tuple_associate_string(ti, "quality", "sequenced");
+		tuple_associate_string(ti, "dumper", info.dumper);
+		tuple_associate_string(ti, "comment", info.comment);
+
 		int length = info.length;
 		if ( length <= 0 )
 			length = info.intro_length + 2 * info.loop_length;
@@ -222,24 +232,24 @@
 			length = audcfg.loop_length * 1000;
 		else if ( length >= fade_threshold )
 			length += fade_length;
-		ti->length = length;
+		tuple_associate_int(ti, "length", length);
 	}
 	return ti;
 }
 
-static char* format_and_free_ti( TitleInput* ti, int* length )
+static char* format_and_free_ti( Tuple* ti, int* length )
 {
-	char* result = xmms_get_titlestring( xmms_get_gentitle_format(), ti );
+	char* result = tuple_formatter_process_string(ti, cfg.gentitle_format);
 	if ( result )
-		*length = ti->length;
-	bmp_title_input_free( ti );
-	
+		*length = tuple_get_int(ti, "length");
+	mowgli_object_unref((void *) ti);
+
 	return result;
 }
 
-static TitleInput *get_song_tuple( gchar *path )
+static Tuple *get_song_tuple( gchar *path )
 {
-	TitleInput* result = 0;
+	Tuple* result = 0;
 	File_Handler fh( path );
 	if ( !fh.load( gme_info_only ) )
 	{
@@ -255,7 +265,7 @@
 	*length = -1;
 	*title = NULL;
 	
-	TitleInput* ti = get_song_tuple( path );
+	Tuple* ti = get_song_tuple( path );
 	if ( ti )
 		*title = format_and_free_ti( ti, length );
 }
@@ -362,7 +372,7 @@
 	{
 		if ( fh.type == gme_spc_type && audcfg.ignore_spc_length )
 			info.length = -1;
-		TitleInput* ti = get_track_ti( fh.path, info, fh.track );
+		Tuple* ti = get_track_ti( fh.path, info, fh.track );
 		if ( ti )
 		{
 			char* title = format_and_free_ti( ti, &length );
--- a/src/cue/cuesheet.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/cue/cuesheet.c	Fri Aug 10 14:23:20 2007 +0300
@@ -31,7 +31,6 @@
 #include <audacious/util.h>
 #include <audacious/strings.h>
 #include <audacious/main.h>
-#include <audacious/strings.h>
 
 #define MAX_CUE_LINE_LENGTH 1000
 #define MAX_CUE_TRACKS 1000
@@ -46,8 +45,8 @@
 static void seek(InputPlayback *data, gint time);
 static void stop(InputPlayback *data);
 static void cue_pause(InputPlayback *data, short);
-static TitleInput *get_tuple(gchar *uri);
-static TitleInput *get_tuple_uri(gchar *uri);
+static Tuple *get_tuple(gchar *uri);
+static Tuple *get_tuple_uri(gchar *uri);
 static void get_song_info(gchar *uri, gchar **title, gint *length);
 static void cue_init(void);
 static void cue_cleanup(void);
@@ -222,9 +221,9 @@
 	play_cue_uri(data, uri);
 }
 
-static TitleInput *get_tuple(gchar *uri)
+static Tuple *get_tuple(gchar *uri)
 {
-	TitleInput *ret;
+	Tuple *ret;
 
 	/* this isn't a cue:// uri? */
 	if (strncasecmp("cue://", uri, 6))
@@ -238,7 +237,14 @@
 	return get_tuple_uri(uri);
 }
 
-static TitleInput *get_tuple_uri(gchar *uri)
+static void _tuple_copy_field(Tuple *tuple, Tuple *tuple2, const gchar *field)
+{
+    const gchar *str = tuple_get_string(tuple, field);
+    tuple_disassociate(tuple2, field);
+    tuple_associate_string(tuple2, field, str);
+}
+
+static Tuple *get_tuple_uri(gchar *uri)
 {
     gchar *path2 = g_strdup(uri + 6);
     gchar *_path = strchr(path2, '?');
@@ -247,7 +253,7 @@
 	ProbeResult *pr;
 	InputPlugin *dec;
 
-	TitleInput *phys_tuple, *out;
+	Tuple *phys_tuple, *out;
 
         if (_path != NULL && *_path == '?')
         {
@@ -273,34 +279,37 @@
 	else
 		phys_tuple = input_get_song_tuple(cue_file);
 
-	out = bmp_title_input_new();
+    if(!phys_tuple)
+        return NULL;
+
+    out = tuple_new();
 
-    if(!phys_tuple)
-        return out;
+    _tuple_copy_field(phys_tuple, out, "file-path");
+    _tuple_copy_field(phys_tuple, out, "file-name");
+    _tuple_copy_field(phys_tuple, out, "file-exit");
+    _tuple_copy_field(phys_tuple, out, "codec");
+    _tuple_copy_field(phys_tuple, out, "quality");
+    _tuple_copy_field(phys_tuple, out, "copyright");
+    _tuple_copy_field(phys_tuple, out, "comment");
 
-    if(phys_tuple->file_path)
-        out->file_path = g_strdup(phys_tuple->file_path);
-    if(phys_tuple->file_name)
-        out->file_name = g_strdup(phys_tuple->file_name);
-    if(phys_tuple->file_ext)
-        out->file_ext = g_strdup(phys_tuple->file_ext);
-	out->length = phys_tuple->length;
+    tuple_associate_int(out, "length", tuple_get_int(phys_tuple, "length"));
+
+    mowgli_object_unref(phys_tuple);
 
-	bmp_title_input_free(phys_tuple);
-
-	out->track_name = g_strdup(cue_tracks[track].title);
-	out->performer = g_strdup(cue_tracks[track].performer ?
+    tuple_associate_string(out, "title", cue_tracks[track].title);
+    tuple_associate_string(out, "artist", cue_tracks[track].performer ?
 				  cue_tracks[track].performer : cue_performer);
-	out->album_name = g_strdup(cue_title);
-	out->genre = g_strdup(cue_genre);
-	out->year = cue_year ? atoi(cue_year) : 0;
-	out->track_number = track + 1;
-	return out;
+    tuple_associate_string(out, "album", cue_title);
+    tuple_associate_string(out, "genre", cue_genre);
+    tuple_associate_int(out, "year", atoi(cue_year));
+    tuple_associate_int(out, "track-number", track + 1);
+
+    return out;
 }
 
 static void get_song_info(gchar *uri, gchar **title, gint *length)
 {
-	TitleInput *tuple;
+	Tuple *tuple;
 
 	/* this isn't a cue:// uri? */
 	if (strncasecmp("cue://", uri, 6))
@@ -314,10 +323,10 @@
 
 	g_return_if_fail(tuple != NULL);
 
-	*title = xmms_get_titlestring(xmms_get_gentitle_format(), tuple);
-	*length = tuple->length;
+	*title = tuple_formatter_process_string(tuple, cfg.gentitle_format);
+	*length = tuple_get_int(tuple, "length");
 
-	bmp_title_input_free(tuple);
+	mowgli_object_unref(tuple);
 }
 
 static void seek(InputPlayback * data, gint time)
--- a/src/flacng/flacng.h	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/flacng/flacng.h	Fri Aug 10 14:23:20 2007 +0300
@@ -21,6 +21,7 @@
 
 #include <glib.h>
 #include <audacious/plugin.h>
+#include <audacious/main.h>
 #include <audacious/vfs.h>
 #include <audacious/i18n.h>
 # include "config.h"
@@ -79,6 +80,7 @@
 
 
 typedef struct callback_info {
+    GMutex* mutex;
     gint32* output_buffer;
     gint32* write_pointer;
     guint buffer_free;
--- a/src/flacng/plugin.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/flacng/plugin.c	Fri Aug 10 14:23:20 2007 +0300
@@ -180,8 +180,11 @@
 
     _DEBUG("Testing fd for file: %s", filename);
 
+    INFO_LOCK(test_info);
+
     if (FALSE == read_metadata(fd, test_decoder, test_info)) {
         _DEBUG("File not handled by this plugin!");
+        INFO_UNLOCK(test_info);
         _LEAVE FALSE;
     }
 
@@ -190,6 +193,7 @@
      */
     if (FALSE == test_info->metadata_changed) {
         _DEBUG("No metadata found in stream");
+        INFO_UNLOCK(test_info);
         _LEAVE FALSE;
     }
 
@@ -207,6 +211,7 @@
     if (MAX_SUPPORTED_CHANNELS < test_info->stream.channels) {
         _ERROR("This number of channels (%d) is currently not supported, stream not handled by this plugin",
             test_info->stream.channels);
+        INFO_UNLOCK(test_info);
         _LEAVE FALSE;
     }
 
@@ -215,6 +220,7 @@
         (8 != test_info->stream.bits_per_sample)) {
         _ERROR("This number of bits (%d) is currently not supported, stream not handled by this plugin",
             test_info->stream.bits_per_sample);
+        INFO_UNLOCK(test_info);
         _LEAVE FALSE;
     }
 
@@ -225,6 +231,7 @@
     _DEBUG("Accepting file %s", filename);
 
     reset_info(test_info, FALSE);
+    INFO_UNLOCK(test_info);
 
     _LEAVE TRUE;
 }
@@ -647,11 +654,14 @@
         _LEAVE;
     }
 
+    INFO_LOCK(test_info);
+
     if (FALSE == read_metadata(fd, test_decoder, test_info)) {
         _ERROR("Could not read file info!");
         *length = -1;
         *title = g_strdup("");
         reset_info(test_info, TRUE);
+        INFO_UNLOCK(test_info);
         _LEAVE;
     }
 
@@ -670,16 +680,17 @@
     *title = get_title(filename, test_info);
 
     reset_info(test_info, TRUE);
+    INFO_UNLOCK(test_info);
 
     _LEAVE;
 }
 
 /* --- */
 
-TitleInput *flac_get_song_tuple(gchar* filename) {
+Tuple *flac_get_song_tuple(gchar* filename) {
 
     VFSFile *fd;
-    TitleInput *tuple;
+    Tuple *tuple;
 
     _ENTER;
 
@@ -692,15 +703,19 @@
         _LEAVE NULL;
     }
 
+    INFO_LOCK(test_info);
+
     if (FALSE == read_metadata(fd, test_decoder, test_info)) {
         _ERROR("Could not read metadata tuple for file <%s>", filename);
         reset_info(test_info, TRUE);
+        INFO_UNLOCK(test_info);
         _LEAVE NULL;
     }
 
     tuple = get_tuple(filename, test_info);
 
     reset_info(test_info, TRUE);
+    INFO_UNLOCK(test_info);
 
     _LEAVE tuple;
 }
--- a/src/flacng/plugin.h	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/flacng/plugin.h	Fri Aug 10 14:23:20 2007 +0300
@@ -10,6 +10,6 @@
 void flac_pause(InputPlayback* input, gshort p);
 void flac_seek(InputPlayback* input, gint time);
 void flac_get_song_info(gchar* filename, gchar** title, gint* length);
-TitleInput *flac_get_song_tuple(gchar* filename);
+Tuple *flac_get_song_tuple(gchar* filename);
 
 #endif
--- a/src/flacng/tools.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/flacng/tools.c	Fri Aug 10 14:23:20 2007 +0300
@@ -66,6 +66,8 @@
     info->replaygain.album_peak = NULL;
     reset_info(info, FALSE);
 
+    info->mutex = g_mutex_new();
+
     _DEBUG("Playback buffer allocated for %d samples, %d bytes", BUFFER_SIZE_SAMP, BUFFER_SIZE_BYTE);
 
     _LEAVE info;
@@ -229,48 +231,36 @@
 
 /* --- */
 
-static const gchar* get_extension(const gchar* filename) {
-
-    _ENTER;
+Tuple* get_tuple(const gchar* filename, callback_info* info) {
 
-    const gchar *ext = strrchr(filename, '.');
-    _LEAVE ext ? ext + 1 : NULL;
-}
-
-/* --- */
-
-TitleInput* get_tuple(const gchar* filename, callback_info* info) {
-
-    TitleInput *out;
+    Tuple *out;
 
     _ENTER;
 
     _DEBUG("Using callback_info %s", info->name);
 
-    out = bmp_title_input_new();
-
-    out->file_name = g_path_get_basename(filename);
-    out->file_ext = get_extension(filename);
-    out->file_path = g_path_get_dirname(filename);
+    out = tuple_new_from_filename(filename);
 
-    out->performer = g_strdup(info->comment.artist);
-    out->track_name = g_strdup(info->comment.title);
-    out->album_name = g_strdup(info->comment.album);
-    out->genre = g_strdup(info->comment.genre);
+    tuple_associate_string(out, "artist", info->comment.artist);
+    tuple_associate_string(out, "title", info->comment.title);
+    tuple_associate_string(out, "album", info->comment.album);
+    tuple_associate_string(out, "genre", info->comment.genre);
+
     if (info->comment.tracknumber != NULL)
-        out->track_number = atoi(info->comment.tracknumber);
+        tuple_associate_int(out, "track-number", atoi(info->comment.tracknumber));
+
     if (info->comment.date != NULL)
-        out->year = atoi(info->comment.date);
+        tuple_associate_int(out, "year", atoi(info->comment.date));
 
     /*
      * Calculate the stream length (milliseconds)
      */
     if (0 == info->stream.samplerate) {
         _ERROR("Invalid sample rate for stream!");
-        out->length = -1;
+        tuple_associate_int(out, "length", -1);
     } else {
-        out->length = (info->stream.samples / info->stream.samplerate) * 1000;
-        _DEBUG("Stream length: %d seconds", out->length / 1000);
+        tuple_associate_int(out, "length", (info->stream.samples / info->stream.samplerate) * 1000);
+        _DEBUG("Stream length: %d seconds", tuple_get_int(out, "length"));
     }
 
     _DEBUG("Tuple created: [%p]", out);
@@ -282,7 +272,7 @@
 
 gchar* get_title(const gchar* filename, callback_info* info) {
 
-    TitleInput *input;
+    Tuple *input;
     gchar *title;
 
     _ENTER;
@@ -291,10 +281,9 @@
 
     input = get_tuple(filename, info);
 
-    if (!(title = xmms_get_titlestring(xmms_get_gentitle_format(), input)))
-        title = g_strdup(input->file_name);
+    title = tuple_formatter_process_string(input, cfg.gentitle_format);
 
-    bmp_title_input_free(input);
+    mowgli_object_unref(input);
 
     _DEBUG("Title created: <%s>", title);
 
--- a/src/flacng/tools.h	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/flacng/tools.h	Fri Aug 10 14:23:20 2007 +0300
@@ -23,11 +23,26 @@
 #include <FLAC/all.h>
 #include "flacng.h"
 #include "flac_compat.h"
+#include "debug.h"
+
+#define INFO_LOCK(__info) \
+    do { \
+        _DEBUG("Trying to lock info %s", (__info)->name); \
+        g_mutex_lock((__info)->mutex); \
+        _DEBUG("Locked info %s", (__info)->name); \
+    } while(0)
+
+#define INFO_UNLOCK(__info) \
+    do { \
+        _DEBUG("Unlocking info %s", (__info)->name); \
+        g_mutex_unlock((__info)->mutex); \
+        _DEBUG("Unlocked info %s", (__info)->name); \
+    } while(0)
 
 callback_info* init_callback_info(gchar* name);
 void reset_info(callback_info* info, gboolean close_fd);
 gchar* get_title(const gchar* filename, callback_info* info);
-TitleInput *get_tuple(const gchar *filename, callback_info* info);
+Tuple *get_tuple(const gchar *filename, callback_info* info);
 void add_comment(callback_info* info, gchar* key, gchar* value);
 gboolean read_metadata(VFSFile* fd, FLAC__StreamDecoder* decoder, callback_info* info);
 
--- a/src/madplug/configure.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/madplug/configure.c	Fri Aug 10 14:23:20 2007 +0300
@@ -257,10 +257,6 @@
     gtk_box_pack_start(GTK_BOX(title_id3_box), title_id3_entry, TRUE, TRUE,
                        0);
 
-    title_tag_desc = xmms_titlestring_descriptions("pafFetnygc", 2);
-    gtk_widget_set_sensitive(title_tag_desc, audmad_config.title_override);
-    gtk_box_pack_start(GTK_BOX(vbox2), title_tag_desc, FALSE,
-                       FALSE, 0);
     gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2,
                              gtk_label_new(_("Title")));
 
--- a/src/madplug/decoder.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/madplug/decoder.c	Fri Aug 10 14:23:20 2007 +0300
@@ -192,7 +192,7 @@
     info->duration = mad_timer_zero; // should be cleared before loop, if we use it as break condition.
 
     if(info->fileinfo_request == TRUE) {
-        info->tuple->length = -1;
+        tuple_associate_int(info->tuple, "length", -1);
         info->fileinfo_request = FALSE;
     }
 
@@ -270,11 +270,13 @@
             g_message("size = %d", stream.next_frame - stream.this_frame);
 #endif
 #endif
-            if(info->tuple->length == -1)
+            if(tuple_get_int(info->tuple, "length") == -1)
                 mad_timer_add(&info->duration, header.duration);
             else {
-                info->duration.seconds = info->tuple->length / 1000;
-                info->duration.fraction = info->tuple->length % 1000;
+                gint length = tuple_get_int(info->tuple, "length");
+
+                info->duration.seconds = length / 1000;
+                info->duration.fraction = length % 1000;
             }
             data_used += stream.next_frame - stream.this_frame;
             if (info->frames == 1) {
@@ -355,7 +357,7 @@
 #ifdef DEBUG
                 g_message("info->frames = %d", info->frames);
 #endif
-                if(info->tuple->length == -1) {
+                if(tuple_get_int(info->tuple, "length") == -1) {
                     if(xing_bitrate > 0.0) {
                         /* calc duration with xing info */
                         double tmp = 8 * (double)info->xing.bytes * 1000 / xing_bitrate;
@@ -369,8 +371,10 @@
                     }
                 }
                 else {
-                    info->duration.seconds = info->tuple->length / 1000;
-                    info->duration.fraction = info->tuple->length % 1000;
+                    gint length = tuple_get_int(info->tuple, "length");
+
+                    info->duration.seconds = length / 1000;
+                    info->duration.fraction = length % 1000;
                 }
 #ifdef DEBUG
                 g_message("using fast playtime calculation");
@@ -478,9 +482,9 @@
     /* set mainwin title */
     if (info->title)
         g_free(info->title);
-    info->title = xmms_get_titlestring(audmad_config.title_override == TRUE ?
-                                       audmad_config.id3_format : xmms_get_gentitle_format(), info->tuple);
-    
+    info->title = tuple_formatter_process_string(info->tuple, audmad_config.title_override == TRUE ?
+                                       audmad_config.id3_format : cfg.gentitle_format);
+
     tlen = (gint) mad_timer_count(info->duration, MAD_UNITS_MILLISECONDS),
         mad_plugin->set_info(info->title,
                              (tlen == 0 || info->size <= 0) ? -1 : tlen,
@@ -704,7 +708,7 @@
     g_message("e: decode");
 #endif                          /* DEBUG */
 
-    bmp_title_input_free(info->tuple);
+    mowgli_object_unref(info->tuple);
     info->tuple = NULL;
 
     info->playback->output->close_audio();
--- a/src/madplug/input.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/madplug/input.c	Fri Aug 10 14:23:20 2007 +0300
@@ -280,7 +280,7 @@
     return ret;
 }
 
-gchar *input_id3_get_string(struct id3_tag * tag, char *frame_name)
+gchar *input_id3_get_string(struct id3_tag * tag, const gchar *frame_name)
 {
     gchar *rtn0 = NULL, *rtn = NULL;
     const id3_ucs4_t *string_const = NULL;
@@ -341,15 +341,24 @@
     return rtn;
 }
 
+static void input_set_and_free_tag(struct id3_tag *tag, Tuple *tuple, const gchar *frame, const gchar *tuple_name)
+{
+    gchar *scratch = input_id3_get_string(tag, frame);
+
+    tuple_associate_string(tuple, tuple_name, scratch);
+    tuple_associate_string(tuple, frame, scratch);
+
+    g_free(scratch);
+}
 
 static void input_alloc_tag(struct mad_info_t *info)
 {
-    TitleInput *title_input;
+    Tuple *title_input;
 
     if (info->tuple == NULL) {
-        title_input = bmp_title_input_new();
+        title_input = tuple_new();
         info->tuple = title_input;
-        info->tuple->length = -1; //will be refferd in decoder.c
+        tuple_associate_int(info->tuple, "length", -1);
     }
     else
         title_input = info->tuple;
@@ -362,14 +371,14 @@
 {
     gchar *string = NULL;
     gchar *realfn = NULL;
-    TitleInput *title_input;
+    Tuple *title_input;
     glong curpos = 0;
 
 #ifdef DEBUG
     g_message("f: input_read_tag");
 #endif
     if (info->tuple == NULL) {
-        title_input = bmp_title_input_new();
+        title_input = tuple_new();
         info->tuple = title_input;
     }
     else
@@ -398,21 +407,19 @@
         return;
     }
 
-    title_input->performer =
-        input_id3_get_string(info->tag, ID3_FRAME_ARTIST);
-    title_input->track_name =
-        input_id3_get_string(info->tag, ID3_FRAME_TITLE);
-    title_input->album_name =
-        input_id3_get_string(info->tag, ID3_FRAME_ALBUM);
-    title_input->genre = input_id3_get_string(info->tag, ID3_FRAME_GENRE);
-    title_input->comment =
-        input_id3_get_string(info->tag, ID3_FRAME_COMMENT);
+    input_set_and_free_tag(info->tag, title_input, ID3_FRAME_ARTIST, "artist");
+    input_set_and_free_tag(info->tag, title_input, ID3_FRAME_TITLE, "title");
+    input_set_and_free_tag(info->tag, title_input, ID3_FRAME_ALBUM, "album");
+    input_set_and_free_tag(info->tag, title_input, ID3_FRAME_GENRE, "genre");
+    input_set_and_free_tag(info->tag, title_input, ID3_FRAME_COMMENT, "comment");
+
     string = input_id3_get_string(info->tag, ID3_FRAME_TRACK);
     if (string) {
-        title_input->track_number = atoi(string);
+        tuple_associate_int(title_input, "track-number", atoi(string));
         g_free(string);
         string = NULL;
     }
+
     // year
     string = NULL;
     string = input_id3_get_string(info->tag, ID3_FRAME_YEAR);   //TDRC
@@ -420,34 +427,44 @@
         string = input_id3_get_string(info->tag, "TYER");
 
     if (string) {
-        title_input->year = atoi(string);
+        tuple_associate_int(title_input, "year", atoi(string));
         g_free(string);
         string = NULL;
     }
 
     // length
-    title_input->length = -1;
     string = input_id3_get_string(info->tag, "TLEN");
     if (string) {
-        title_input->length = atoi(string);
+        tuple_associate_int(title_input, "length", atoi(string));
 #ifdef DEBUG
-        g_message("input_read_tag: TLEN = %d", title_input->length);
+        g_message("input_read_tag: TLEN = %d", atoi(string));
 #endif	
         g_free(string);
         string = NULL;
     }
     
     realfn = g_filename_from_uri(info->filename, NULL, NULL);
-    title_input->file_name = g_strdup(g_basename(realfn ? realfn : info->filename));
-    title_input->file_path = g_path_get_dirname(realfn ? realfn : info->filename);
-    g_free(realfn); realfn = NULL;
-    if ((string = strrchr(title_input->file_name, '.'))) {
-        title_input->file_ext = string + 1;
+    
+    string = g_strdup(g_basename(realfn ? realfn : info->filename));
+    tuple_associate_string(title_input, "file-name", string);
+    g_free(string);
+
+    string = g_path_get_dirname(realfn ? realfn : info->filename);
+    tuple_associate_string(title_input, "file-path", string);
+    g_free(string);
+
+    if ((string = strrchr(realfn ? realfn : info->filename, '.'))) {
         *string = '\0';         // make filename end at dot.
+        tuple_associate_string(title_input, "file-ext", string + 1);
     }
 
-    info->title = xmms_get_titlestring(audmad_config.title_override == TRUE ?
-        audmad_config.id3_format : xmms_get_gentitle_format(), title_input);
+    g_free(realfn); realfn = NULL;
+
+    tuple_associate_string(title_input, "codec", "MPEG Audio (MP3)");
+    tuple_associate_string(title_input, "quality", "lossy");
+
+    info->title = tuple_formatter_process_string(title_input, audmad_config.title_override == TRUE ?
+        audmad_config.id3_format : cfg.gentitle_format);
 
     // for connection via proxy, we have to stop transfer once. I can't explain the reason.
     if (info->infile != NULL) {
@@ -458,11 +475,12 @@
 #ifdef DEBUG
     g_message("e: input_read_tag");
 #endif
-
 }
 
 void input_process_remote_metadata(struct mad_info_t *info)
 {
+    gboolean metadata = FALSE;
+
     if(info->remote && mad_timer_count(info->duration, MAD_UNITS_SECONDS) <= 0){
         gchar *tmp = NULL;
 #ifdef DEBUG
@@ -470,32 +488,41 @@
         g_message("process_remote_meta");
 #endif
 #endif
+
         g_free(info->title);
         info->title = NULL;
-        g_free(info->tuple->track_name);
-        info->tuple->track_name = NULL;
-        g_free(info->tuple->album_name);
-        info->tuple->album_name = NULL;
+        tuple_disassociate(info->tuple, "title");
+        tuple_disassociate(info->tuple, "album");
 
         tmp = vfs_get_metadata(info->infile, "track-name");
         if(tmp){
-            info->tuple->track_name = str_to_utf8(tmp);
-            info->title = g_strdup(info->tuple->track_name);
+            metadata = TRUE;
+            gchar *scratch;
+
+            scratch = str_to_utf8(tmp);
+            tuple_associate_string(info->tuple, "title", scratch);
+            g_free(scratch);
+
             g_free(tmp);
             tmp = NULL;
         }
 
         tmp = vfs_get_metadata(info->infile, "stream-name");
         if(tmp){
-            info->tuple->album_name = str_to_utf8(tmp);
+            metadata = TRUE;
+            gchar *scratch;
+
+            scratch = str_to_utf8(tmp);
+            tuple_associate_string(info->tuple, "album", scratch);
+            tuple_associate_string(info->tuple, "stream", scratch);
+            g_free(scratch);
+
             g_free(tmp);
             tmp = NULL;
         }
 
-        if (info->tuple->track_name && info->tuple->album_name)
-            tmp = g_strdup_printf("%s (%s)", info->tuple->track_name, info->tuple->album_name);
-        else if (info->tuple->album_name)
-            tmp = g_strdup(info->tuple->album_name);
+        if (metadata)
+            tmp = tuple_formatter_process_string(info->tuple, "${?title:${title}}${?stream: (${stream})");
         else {
             gchar *realfn = g_filename_from_uri(info->filename, NULL, NULL);
             gchar *tmp2 = g_path_get_basename(realfn ? realfn : info->filename); // info->filename is uri. --yaz
@@ -637,7 +664,7 @@
         g_free(info->mp3gain_minmax_str);
 
     if (info->tuple) {
-        bmp_title_input_free(info->tuple);
+        mowgli_object_unref(info->tuple);
         info->tuple = NULL;
     }
 
--- a/src/madplug/input.h	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/madplug/input.h	Fri Aug 10 14:23:20 2007 +0300
@@ -28,6 +28,6 @@
 gboolean input_get_info(struct mad_info_t *songinfo, gboolean fast_scan);
 gint input_get_data(struct mad_info_t *songinfo, guchar * buffer,
                     gint buffer_size);
-gchar *input_id3_get_string(struct id3_tag *tag, char *frame_name);
+gchar *input_id3_get_string(struct id3_tag *tag, const gchar *frame_name);
 
 #endif                          /* ! INPUT_H */
--- a/src/madplug/plugin.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/madplug/plugin.c	Fri Aug 10 14:23:20 2007 +0300
@@ -515,17 +515,17 @@
     }
 
     if (input_get_info(&myinfo, info.remote ? TRUE : audmad_config.fast_play_time_calc) == TRUE) {
-        if(myinfo.tuple->track_name)
-            *title = strdup(myinfo.tuple->track_name);
+        if(tuple_get_string(myinfo.tuple, "track-name"))
+            *title = g_strdup(tuple_get_string(myinfo.tuple, "track-name"));
         else
-            *title = strdup(url);
-        if(myinfo.tuple->length == -1)
+            *title = g_strdup(url);
+        if(tuple_get_int(myinfo.tuple, "length") == -1)
             *length = mad_timer_count(myinfo.duration, MAD_UNITS_MILLISECONDS);
         else
-            *length = myinfo.tuple->length;
+            *length = tuple_get_int(myinfo.tuple, "length");
     }
     else {
-        *title = strdup(url);
+        *title = g_strdup(url);
         *length = -1;
     }
     input_term(&myinfo);
@@ -552,10 +552,10 @@
     }
 
     if (input_get_info(&myinfo, info.remote ? TRUE : audmad_config.fast_play_time_calc) == TRUE) {
-        if(myinfo.tuple->length == -1)
+        if(tuple_get_int(myinfo.tuple, "length") == -1)
             *length = mad_timer_count(myinfo.duration, MAD_UNITS_MILLISECONDS);
         else
-            *length = myinfo.tuple->length;
+            *length = tuple_get_int(myinfo.tuple, "length");
     }
     else {
         *length = -1;
@@ -628,11 +628,16 @@
 extern void audmad_get_file_info(char *filename);
 extern void audmad_configure();
 
+static void __set_and_free(Tuple *tuple, gchar *name, gchar *value)
+{
+    tuple_associate_string(tuple, name, value);
+    g_free(value);
+}
 
 // tuple stuff
-static TitleInput *__audmad_get_song_tuple(char *filename, VFSFile *fd)
+static Tuple *__audmad_get_song_tuple(char *filename, VFSFile *fd)
 {
-    TitleInput *tuple = NULL;
+    Tuple *tuple = NULL;
     gchar *string = NULL;
     gchar *realfn = NULL;
 
@@ -651,7 +656,7 @@
     if(info.remote && mad_timer_count(info.duration, MAD_UNITS_SECONDS) <= 0){
         if((fd && vfs_is_streaming(fd)) || (info.playback && info.playback->playing)) {
             gchar *tmp = NULL;
-            tuple = bmp_title_input_new();
+            tuple = tuple_new();
 
 #ifdef DEBUG
             if(info.playback)
@@ -659,14 +664,24 @@
 #endif
             tmp = vfs_get_metadata(info.infile ? info.infile : fd, "track-name");
             if(tmp){
-                tuple->track_name = str_to_utf8(tmp);
+                gchar *scratch;
+
+                scratch = str_to_utf8(tmp);
+                tuple_associate_string(tuple, "title", scratch);
                 g_free(tmp);
+                g_free(scratch);
+
                 tmp = NULL;
             }
             tmp = vfs_get_metadata(info.infile ? info.infile : fd, "stream-name");
             if(tmp){
-                tuple->album_name = str_to_utf8(tmp);
+                gchar *scratch;
+
+                scratch = str_to_utf8(tmp);
+                tuple_associate_string(tuple, "title", scratch);
                 g_free(tmp);
+                g_free(scratch);
+
                 tmp = NULL;
             }
 
@@ -675,13 +690,13 @@
             g_message("audmad_get_song_tuple: stream_name = %s", tuple->album_name);
 #endif
             realfn = g_filename_from_uri(filename, NULL, NULL);
-            tuple->file_name = g_path_get_basename(realfn ? realfn : filename);
-            tuple->file_path = g_path_get_dirname(realfn ? realfn : filename);
-            tuple->file_ext = extname(realfn ? realfn : filename);
+            __set_and_free(tuple, "file-name", g_path_get_basename(realfn ? realfn : filename));
+            __set_and_free(tuple, "file-path", g_path_get_dirname(realfn ? realfn : filename));
+            tuple_associate_string(tuple, "file-ext", extname(realfn ? realfn : filename));
             g_free(realfn); realfn = NULL;
 
-            tuple->length = -1;
-            tuple->mtime = 0; // this indicates streaming
+            tuple_associate_int(tuple, "length", -1);
+            tuple_associate_int(tuple, "mtime", 0); // this indicates streaming
 #ifdef DEBUG
             g_message("get_song_tuple: remote: tuple");
 #endif
@@ -693,8 +708,6 @@
         return NULL;
     } /* info.remote  */
 
-    tuple = bmp_title_input_new();
-
     // if !fd, pre-open the file with vfs_fopen() and reuse fd.
     if(!fd) {
         fd = vfs_fopen(filename, "rb");
@@ -703,18 +716,18 @@
         local_fd = TRUE;
     }
 
+    tuple = tuple_new();
+    tuple_associate_int(tuple, "length", -1);
+
     id3file = id3_file_vfsopen(fd, ID3_FILE_MODE_READONLY);
 
     if (id3file) {
 
         tag = id3_file_tag(id3file);
         if (tag) {
-            tuple->performer =
-                input_id3_get_string(tag, ID3_FRAME_ARTIST);
-            tuple->album_name =
-                input_id3_get_string(tag, ID3_FRAME_ALBUM);
-            tuple->track_name =
-                input_id3_get_string(tag, ID3_FRAME_TITLE);
+            __set_and_free(tuple, "artist", input_id3_get_string(tag, ID3_FRAME_ARTIST));
+            __set_and_free(tuple, "album", input_id3_get_string(tag, ID3_FRAME_ALBUM));
+            __set_and_free(tuple, "title", input_id3_get_string(tag, ID3_FRAME_TITLE));
 
             // year
             string = NULL;
@@ -723,23 +736,22 @@
                 string = input_id3_get_string(tag, "TYER");
 
             if (string) {
-                tuple->year = atoi(string);
+                tuple_associate_int(tuple, "year", atoi(string));
                 g_free(string);
                 string = NULL;
             }
             realfn = g_filename_from_uri(filename, NULL, NULL);
-            tuple->file_name = g_path_get_basename(realfn ? realfn : filename);
-            tuple->file_path = g_path_get_dirname(realfn ? realfn : filename);
-            tuple->file_ext = extname(realfn ? realfn : filename);
+            __set_and_free(tuple, "file-name", g_path_get_basename(realfn ? realfn : filename));
+            __set_and_free(tuple, "file-path", g_path_get_dirname(realfn ? realfn : filename));
+            tuple_associate_string(tuple, "file-ext", extname(realfn ? realfn : filename));
             g_free(realfn); realfn = NULL;
 
             // length
-            tuple->length = -1;
             string = input_id3_get_string(tag, "TLEN");
             if (string) {
-                tuple->length = atoi(string);
+                tuple_associate_int(tuple, "length", atoi(string));
 #ifdef DEBUG
-                g_message("get_song_tuple: TLEN = %d", tuple->length);
+                g_message("get_song_tuple: TLEN = %d", tuple_get_int(tuple, "length"));
 #endif
                 g_free(string);
                 string = NULL;
@@ -748,47 +760,47 @@
                 char *dummy = NULL;
                 int length = 0;
                 audmad_get_song_length(filename, &length, fd);
-                tuple->length = length;
+                tuple_associate_int(tuple, "length", length);
                 g_free(dummy);
             }
 
             // track number
             string = input_id3_get_string(tag, ID3_FRAME_TRACK);
             if (string) {
-                tuple->track_number = atoi(string);
+                tuple_associate_int(tuple, "track-number", atoi(string));
                 g_free(string);
                 string = NULL;
             }
             // genre
-            tuple->genre = input_id3_get_string(tag, ID3_FRAME_GENRE);
+            __set_and_free(tuple, "genre", input_id3_get_string(tag, ID3_FRAME_GENRE));
+            __set_and_free(tuple, "comment", input_id3_get_string(tag, ID3_FRAME_COMMENT));
 #ifdef DEBUG
             g_message("genre = %s", tuple->genre);
 #endif
-            // comment
-            tuple->comment =
-                input_id3_get_string(tag, ID3_FRAME_COMMENT);
-
         }
         id3_file_close(id3file);
     } // id3file
     else { // no id3tag
         realfn = g_filename_from_uri(filename, NULL, NULL);
-        tuple->file_name = g_path_get_basename(realfn ? realfn : filename);
-        tuple->file_path = g_path_get_dirname(realfn ? realfn : filename);
-        tuple->file_ext = extname(realfn ? realfn : filename);
+        __set_and_free(tuple, "file-name", g_path_get_basename(realfn ? realfn : filename));
+        __set_and_free(tuple, "file-path", g_path_get_dirname(realfn ? realfn : filename));
+        tuple_associate_string(tuple, "file-ext", extname(realfn ? realfn : filename));
         g_free(realfn); realfn = NULL;
         // length
         {
             char *dummy = NULL;
             int length = 0;
-            if(tuple->length == -1) {
+            if(tuple_get_int(tuple, "length") == -1) {
                 audmad_get_song_length(filename, &length, fd);
-                tuple->length = length;
+                tuple_associate_int(tuple, "length", length);
             }
             g_free(dummy);
         }
     }
 
+    tuple_associate_string(tuple, "quality", "lossy");
+    tuple_associate_string(tuple, "codec", "MPEG Audio (MP3)");
+
     if(local_fd)
         vfs_fclose(fd);
 
@@ -798,12 +810,12 @@
     return tuple;
 }
 
-static TitleInput *audmad_get_song_tuple(char *filename)
+static Tuple *audmad_get_song_tuple(char *filename)
 {
     return __audmad_get_song_tuple(filename, NULL);
 }
 
-static TitleInput *audmad_probe_for_tuple(char *filename, VFSFile *fd)
+static Tuple *audmad_probe_for_tuple(char *filename, VFSFile *fd)
 {
     if (!audmad_is_our_fd(filename, fd))
         return NULL;
--- a/src/madplug/plugin.h	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/madplug/plugin.h	Fri Aug 10 14:23:20 2007 +0300
@@ -37,7 +37,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <audacious/plugin.h>
-#include <audacious/titlestring.h>
+#include <audacious/main.h>
 #include <audacious/util.h>
 #include <audacious/strings.h>
 #include <audacious/vfs.h>
@@ -74,7 +74,7 @@
     struct id3_tag *tag;
     struct id3_file *id3file;
     struct xing xing;
-    TitleInput *tuple;          /* audacious tuple data */
+    Tuple *tuple;          /* audacious tuple data */
     gchar *prev_title;           /* used to optimize set_info calls */
 
     /* replay parameters */
--- a/src/musepack/libmpc.cxx	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/musepack/libmpc.cxx	Fri Aug 10 14:23:20 2007 +0300
@@ -346,30 +346,28 @@
     return data->output->output_time();
 }
 
-static TitleInput *mpcGetSongTuple(char* p_Filename)
+static Tuple *mpcGetSongTuple(char* p_Filename)
 {
     VFSFile *input = vfs_fopen(p_Filename, "rb");
-    TitleInput *tuple = NULL;
+    Tuple *tuple = NULL;
 
     if(input)
     {
-        tuple = bmp_title_input_new();
-        gchar *filename_proxy = g_strdup(p_Filename);
-
-        tuple->file_name = g_path_get_basename(filename_proxy);
-        tuple->file_path = g_path_get_dirname(filename_proxy);
-        tuple->file_ext = "mpc";		// XXX: I can't be assed. -nenolod
+        tuple = tuple_new_from_filename(p_Filename);
 
         MpcInfo tags = getTags(p_Filename);
 
-        tuple->date         = g_strdup(tags.date);
-        tuple->track_name   = g_strdup(tags.title);
-        tuple->performer    = g_strdup(tags.artist);
-        tuple->album_name   = g_strdup(tags.album);
-        tuple->track_number = tags.track;
-        tuple->year         = tags.year;
-        tuple->genre        = g_strdup(tags.genre);
-        tuple->comment      = g_strdup(tags.comment);
+        tuple_associate_string(tuple, "date", tags.date);
+        tuple_associate_string(tuple, "title", tags.title);
+        tuple_associate_string(tuple, "artist", tags.artist);
+        tuple_associate_string(tuple, "album", tags.album);
+        tuple_associate_int(tuple, "track-number", tags.track);
+        tuple_associate_int(tuple, "year", tags.year);
+        tuple_associate_string(tuple, "genre", tags.genre);
+        tuple_associate_string(tuple, "comment", tags.comment);
+
+        tuple_associate_string(tuple, "codec", "Musepack");
+        tuple_associate_string(tuple, "quality", "lossy");
 
         freeTags(tags);
 
@@ -378,7 +376,7 @@
         mpc_reader_setup_file_vfs(&reader, input);
         mpc_streaminfo_read(&info, &reader.reader);
 
-        tuple->length = static_cast<int> (1000 * mpc_streaminfo_get_length(&info));
+        tuple_associate_int(tuple, "length", static_cast<int> (1000 * mpc_streaminfo_get_length(&info)));
         vfs_fclose(input);
     }
     else
@@ -733,15 +731,14 @@
 
 static char* mpcGenerateTitle(const MpcInfo& p_Tags, char* p_Filename)
 {
-    TitleInput* tuple = mpcGetSongTuple(p_Filename);
+    Tuple* tuple = mpcGetSongTuple(p_Filename);
+
+    char* title = tuple_formatter_process_string(tuple, cfg.gentitle_format);
 
-    char* title = xmms_get_titlestring (xmms_get_gentitle_format(), tuple);
-    if(!title)
-        title = g_strdup(tuple->file_name);
-    else if (!*title)
-        title = g_strdup(tuple->file_name);
+    if (!*title)
+        title = g_strdup(tuple_get_string(tuple, "file-name"));
 
-    bmp_title_input_free(tuple);
+    mowgli_object_unref((void *) tuple);
     return title;
 }
 
--- a/src/musepack/libmpc.h	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/musepack/libmpc.h	Fri Aug 10 14:23:20 2007 +0300
@@ -8,7 +8,7 @@
 #include "audacious/output.h"
 #include "audacious/util.h"
 #include "audacious/configdb.h"
-#include "audacious/titlestring.h"
+#include "audacious/main.h"
 #include "audacious/vfs.h"
 #include <audacious/i18n.h>
 #include "../../config.h"
@@ -134,7 +134,7 @@
 static void       setOffset(double);
 static bool       isPause();
 static void       setReplaygain(mpc_streaminfo&, mpc_decoder&);
-static TitleInput* mpcGetSongTuple(char *);
+static Tuple *    mpcGetSongTuple(char *);
 
 #ifdef MPC_FIXED_POINT
 inline static int shiftSigned(MPC_SAMPLE_FORMAT val, int shift)
--- a/src/sexypsf/plugin.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/sexypsf/plugin.c	Fri Aug 10 14:23:20 2007 +0300
@@ -18,7 +18,9 @@
 
 #include "audacious/output.h"
 #include "audacious/plugin.h"
-#include "audacious/titlestring.h"
+#include "audacious/main.h"
+#include "audacious/tuple.h"
+#include "audacious/tuple_formatter.h"
 #include "audacious/util.h"
 #include "audacious/vfs.h"
 #include <stdio.h>
@@ -218,18 +220,25 @@
     }
 }
 
-static TitleInput *get_tuple_psf(gchar *fn) {
-    TitleInput *tuple = NULL;
+static Tuple *get_tuple_psf(gchar *fn) {
+    Tuple *tuple = NULL;
     PSFINFO *tmp = sexypsf_getpsfinfo(fn);
 
     if (tmp->length) {
-        tuple = bmp_title_input_new();
-        tuple->length = tmp->length;
-        tuple->performer = g_strdup(tmp->artist);
-        tuple->album_name = g_strdup(tmp->game);
-        tuple->track_name = g_strdup(tmp->title);
-        tuple->file_name = g_path_get_basename(fn);
-        tuple->file_path = g_path_get_dirname(fn);
+        tuple = tuple_new_from_filename(fn);
+	tuple_associate_int(tuple, "length", tmp->length);
+	tuple_associate_string(tuple, "artist", tmp->artist);
+	tuple_associate_string(tuple, "album", tmp->game);
+	tuple_associate_string(tuple, "game", tmp->game);
+        tuple_associate_string(tuple, "title", tmp->title);
+        tuple_associate_string(tuple, "genre", tmp->genre);
+        tuple_associate_string(tuple, "copyright", tmp->copyright);
+        tuple_associate_string(tuple, "quality", "sequenced");
+        tuple_associate_string(tuple, "codec", "PlayStation Audio");
+        tuple_associate_string(tuple, "console", "PlayStation");
+        tuple_associate_string(tuple, "dumper", tmp->psfby);
+        tuple_associate_string(tuple, "comment", tmp->comment);
+
         sexypsf_freepsfinfo(tmp);
     }
 
@@ -238,12 +247,11 @@
 
 static gchar *get_title_psf(gchar *fn) {
     gchar *title = NULL;
-    TitleInput *tinput = get_tuple_psf(fn);
+    Tuple *tuple = get_tuple_psf(fn);
 
-    if (tinput != NULL) {
-        title = xmms_get_titlestring(xmms_get_gentitle_format(),
-                                     tinput);
-        bmp_title_input_free(tinput);
+    if (tuple != NULL) {
+        title = tuple_formatter_process_string(tuple, cfg.gentitle_format);
+        mowgli_object_unref(tuple);
     }
     else
         title = g_path_get_basename(fn);
--- a/src/vorbis/configure.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/vorbis/configure.c	Fri Aug 10 14:23:20 2007 +0300
@@ -10,7 +10,6 @@
 #include <string.h>
 
 #include "audacious/configdb.h"
-#include "audacious/titlestring.h"
 #include "audacious/util.h"
 #include "audacious/plugin.h"
 #include <audacious/i18n.h>
@@ -157,10 +156,6 @@
     gtk_box_pack_start(GTK_BOX(title_tag_box), title_tag_entry, TRUE, TRUE,
                        0);
 
-    title_desc = xmms_titlestring_descriptions("pafFetndgc", 2);
-    gtk_widget_set_sensitive(title_desc, vorbis_cfg.tag_override);
-    gtk_box_pack_start(GTK_BOX(title_tag_vbox), title_desc, FALSE, FALSE, 0);
-
     gtk_notebook_append_page(GTK_NOTEBOOK(notebook), title_frame,
                              gtk_label_new(_("Title")));
 
--- a/src/vorbis/vorbis.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/vorbis/vorbis.c	Fri Aug 10 14:23:20 2007 +0300
@@ -56,7 +56,7 @@
 #include "audacious/output.h"
 #include "audacious/util.h"
 #include "audacious/configdb.h"
-#include "audacious/titlestring.h"
+#include "audacious/main.h"
 #include <audacious/i18n.h>
 #include <audacious/strings.h>
 
@@ -64,7 +64,7 @@
 
 extern vorbis_config_t vorbis_cfg;
 
-static TitleInput *get_song_tuple(gchar *filename);
+static Tuple *get_song_tuple(gchar *filename);
 static int vorbis_check_file(char *filename);
 static int vorbis_check_fd(char *filename, VFSFile *stream);
 static void vorbis_play(InputPlayback *data);
@@ -364,6 +364,7 @@
      */
     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;
@@ -603,15 +604,13 @@
 static void
 vorbis_get_song_info(char *filename, char **title, int *length)
 {
-    TitleInput *tuple = get_song_tuple(filename);
+    Tuple *tuple = get_song_tuple(filename);
 
-    *length = tuple->length;
-    *title = xmms_get_titlestring(vorbis_cfg.tag_override ?
-                                  vorbis_cfg.tag_format :
-                                  xmms_get_gentitle_format(),
-                                  tuple);
+    *length = tuple_get_int(tuple, "length");
+    *title = tuple_formatter_process_string(tuple, vorbis_cfg.tag_override ?
+                                  vorbis_cfg.tag_format : cfg.gentitle_format);
 
-    bmp_title_input_free(tuple);
+    mowgli_object_unref(tuple);
 }
 
 static const gchar *
@@ -730,63 +729,64 @@
     return 2 * ch * samples;
 }
 
+static void _tuple_associate_string(Tuple *tuple, const gchar *field, const gchar *string)
+{
+    gchar *str = str_to_utf8(string);
+
+    tuple_associate_string(tuple, field, str);
+
+    g_free(str);
+}
+
 /*
  * Ok, nhjm449! Are you *happy* now?!  -nenolod
  */
-static TitleInput *
+static Tuple *
 get_tuple_for_vorbisfile(OggVorbis_File * vorbisfile, gchar *filename, gboolean is_stream)
 {
-    TitleInput *tuple = NULL;
+    Tuple *tuple = NULL;
     vorbis_comment *comment;
     gchar *realfn = NULL;
-    tuple = bmp_title_input_new();
-
-    realfn = g_filename_from_uri(filename, NULL, NULL);
-    tuple->file_name = g_path_get_basename(realfn ? realfn : filename);
-    tuple->file_ext = get_extension(realfn ? realfn : filename);
-    tuple->file_path = g_path_get_dirname(realfn ? realfn : filename);
-    g_free(realfn); realfn = NULL;
+    tuple = tuple_new_from_filename(filename);
 
     /* Retrieve the length */
     if (is_stream == FALSE)
-        tuple->length = ov_time_total(vorbisfile, -1) * 1000;
+        tuple_associate_int(tuple, "length", ov_time_total(vorbisfile, -1) * 1000);
     else
-        tuple->length = -1;
+        tuple_associate_int(tuple, "length", -1);
 
     if ((comment = ov_comment(vorbisfile, -1))) {
-        tuple->track_name =
-            str_to_utf8(vorbis_comment_query(comment, "title", 0));
-        tuple->performer =
-            str_to_utf8(vorbis_comment_query(comment, "artist", 0));
-        tuple->album_name =
-            str_to_utf8(vorbis_comment_query(comment, "album", 0));
+        _tuple_associate_string(tuple, "title", vorbis_comment_query(comment, "title", 0));
+        _tuple_associate_string(tuple, "artist", vorbis_comment_query(comment, "artist", 0));
+        _tuple_associate_string(tuple, "album", vorbis_comment_query(comment, "album", 0));
+        _tuple_associate_string(tuple, "date", vorbis_comment_query(comment, "date", 0));
+        _tuple_associate_string(tuple, "genre", vorbis_comment_query(comment, "genre", 0));
+        _tuple_associate_string(tuple, "comment", vorbis_comment_query(comment, "comment", 0));
 
         if (vorbis_comment_query(comment, "tracknumber", 0) != NULL)
-            tuple->track_number =
-                atoi(vorbis_comment_query(comment, "tracknumber", 0));
+            tuple_associate_int(tuple, "track-number", 
+                atoi(vorbis_comment_query(comment, "tracknumber", 0)));
 
-        tuple->date = str_to_utf8(vorbis_comment_query(comment, "date", 0));
-        tuple->genre = str_to_utf8(vorbis_comment_query(comment, "genre", 0));
-        tuple->comment =
-            str_to_utf8(vorbis_comment_query(comment, "comment", 0));
-
-        /* remove any blank tags */
-        REMOVE_NONEXISTANT_TAG(tuple->performer);
-        REMOVE_NONEXISTANT_TAG(tuple->album_name);
-        REMOVE_NONEXISTANT_TAG(tuple->track_name);
-        REMOVE_NONEXISTANT_TAG(tuple->genre);
-        REMOVE_NONEXISTANT_TAG(tuple->comment);
+        tuple_associate_string(tuple, "quality", "lossy");
+        if (comment && comment->vendor)
+        {
+            gchar *codec = g_strdup_printf("Ogg Vorbis [%s]", comment->vendor);
+            tuple_associate_string(tuple, "codec", codec);
+            g_free(codec);
+        }
+        else
+            tuple_associate_string(tuple, "codec", "Ogg Vorbis");
     }
 
     return tuple;
 }
 
-static TitleInput *
+static Tuple *
 get_song_tuple(gchar *filename)
 {
     VFSFile *stream = NULL;
     OggVorbis_File vfile;          /* avoid thread interaction */
-    TitleInput *tuple = NULL;
+    Tuple *tuple = NULL;
     gboolean is_stream = FALSE;
     VFSVorbisFile *fd = NULL;
 
@@ -823,34 +823,34 @@
 {
     /* Caller should hold vf_mutex */
     gchar *displaytitle = NULL;
-    TitleInput *input;
+    Tuple *input;
     gchar *tmp;
 
     input = get_tuple_for_vorbisfile(vorbisfile, filename, vorbis_is_streaming);
 
-    if (!(displaytitle = xmms_get_titlestring(vorbis_cfg.tag_override ?
-                                              vorbis_cfg.tag_format :
-                                              xmms_get_gentitle_format(),
-                                              input))) {
-        displaytitle = g_strdup(input->file_name);
-    }
+    displaytitle = tuple_formatter_process_string(input, vorbis_cfg.tag_override ?
+                                              vorbis_cfg.tag_format : cfg.gentitle_format);
 
     if ((tmp = vfs_get_metadata(((VFSVorbisFile *) vorbisfile->datasource)->fd, "stream-name")) != NULL)
     {
         gchar *old = displaytitle;
-        displaytitle = g_strdup_printf("%s (%s)", displaytitle, tmp);
+
+        tuple_associate_string(input, "stream", tmp);
+        tuple_associate_string(input, "title", old);
+
+        displaytitle = tuple_formatter_process_string(input, "${?title:${title}}${?stream: (${stream})}");
 
 	g_free(old);
 	g_free(tmp);
     }
 
-    bmp_title_input_free(input);
+    mowgli_object_unref(input);
 
     return displaytitle;
 }
 
 static void
-vorbis_aboutbox()
+vorbis_aboutbox(void)
 {
     static GtkWidget *about_window;
 
--- a/src/vtx/about.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/vtx/about.c	Fri Aug 10 14:23:20 2007 +0300
@@ -4,7 +4,6 @@
 
 #include <audacious/output.h>
 #include <audacious/util.h>
-#include <audacious/titlestring.h>
 #include <audacious/vfs.h>
 #include <audacious/strings.h>
 #include <audacious/i18n.h>
--- a/src/vtx/info.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/vtx/info.c	Fri Aug 10 14:23:20 2007 +0300
@@ -1,7 +1,6 @@
 #include <audacious/plugin.h>
 #include <audacious/util.h>
 #include <audacious/output.h>
-#include <audacious/titlestring.h>
 #include <audacious/vfs.h>
 #include <audacious/strings.h>
 #include <audacious/i18n.h>
--- a/src/vtx/vtx.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/vtx/vtx.c	Fri Aug 10 14:23:20 2007 +0300
@@ -21,9 +21,9 @@
 
 #include <audacious/output.h>
 #include <audacious/util.h>
-#include <audacious/titlestring.h>
 #include <audacious/configdb.h>
 #include <audacious/vfs.h>
+#include <audacious/main.h>
 #include <audacious/strings.h>
 #include <audacious/i18n.h>
 
@@ -92,36 +92,38 @@
   return ret;
 }
 
-TitleInput *
+Tuple *
 vtx_get_song_tuple_from_vtx(const gchar *filename, ayemu_vtx_t *in)
 {
-  TitleInput *out = bmp_title_input_new();
+  Tuple *out = tuple_new_from_filename(filename);
   gchar *string;
 
-  out->performer = g_strdup(in->hdr.author);
-  out->track_name = g_strdup(in->hdr.title);
+  tuple_associate_string(out, "artist", in->hdr.author);
+  tuple_associate_string(out, "title", in->hdr.title);
+
+  tuple_associate_int(out, "length", in->hdr.regdata_size / 14 * 1000 / 50);
 
-  out->file_name = g_strdup(g_basename(filename));
-  out->file_path = g_path_get_dirname(filename);
-  if ((string = strrchr(out->file_name, '.')))
-    {
-      out->file_ext = string + 1;
-      *string = '\0';
-    }
+  tuple_associate_string(out, "genre", (in->hdr.chiptype == AYEMU_AY)? "AY chiptunes" : "YM chiptunes");
+  tuple_associate_string(out, "album", in->hdr.from);
+  tuple_associate_string(out, "game", in->hdr.from);
 
-  out->length = in->hdr.regdata_size / 14 * 1000 / 50;
+  tuple_associate_string(out, "quality", "sequenced");
+  tuple_associate_string(out, "codec", in->hdr.tracker);
+  tuple_associate_string(out, "tracker", in->hdr.tracker);
+
+  tuple_associate_int(out, "year", in->hdr.year);
 
   return out;
 }
 
-TitleInput *
+Tuple *
 vtx_get_song_tuple(gchar *filename)
 {
   ayemu_vtx_t tmp;
 
   if (ayemu_vtx_open (&tmp, filename))
     {
-      TitleInput *ti = vtx_get_song_tuple_from_vtx(filename, &tmp);
+      Tuple *ti = vtx_get_song_tuple_from_vtx(filename, &tmp);
       ayemu_vtx_free(&tmp);
       return ti;
     }
@@ -204,7 +206,7 @@
 {
   gchar *filename = playback->filename;
   gchar *buf;
-  TitleInput *ti;
+  Tuple *ti;
 
   memset (&ay, 0, sizeof(ay));
 
@@ -232,8 +234,7 @@
       seek_to = -1;
 
       ti = vtx_get_song_tuple_from_vtx(playback->filename, &vtx);
-
-      buf = xmms_get_titlestring(xmms_get_gentitle_format(), ti);
+      buf = tuple_formatter_process_string(ti, cfg.gentitle_format);
 
       vtx_ip.set_info (buf, vtx.hdr.regdata_size / 14 * 1000 / 50,
  	  	       14 * 50 * 8, freq, bits / 8);
@@ -294,13 +295,13 @@
   (*title) = NULL;
 
   if (ayemu_vtx_open (&tmp, filename)) {
-    TitleInput *ti = vtx_get_song_tuple_from_vtx(filename, &tmp);
+    Tuple *ti = vtx_get_song_tuple_from_vtx(filename, &tmp);
 
-    *title = xmms_get_titlestring(xmms_get_gentitle_format(), ti);
-    *length = ti->length;
+    *title = tuple_formatter_process_string(ti, cfg.gentitle_format);
+    *length = tuple_get_int(ti, "length");
 
     ayemu_vtx_free (&tmp);
-    bmp_title_input_free(ti);
+    mowgli_object_unref(ti);
   }
 }
 
--- a/src/wav/wav-sndfile.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/wav/wav-sndfile.c	Fri Aug 10 14:23:20 2007 +0300
@@ -37,10 +37,12 @@
 #include <math.h>
 #include <stdio.h>
 
-#include <audacious/plugin.h>
-#include <audacious/util.h>
-#include <audacious/titlestring.h>
-#include <audacious/i18n.h>
+#include "audacious/plugin.h"
+#include "audacious/util.h"
+#include "audacious/i18n.h"
+#include "audacious/main.h"
+#include "audacious/tuple.h"
+#include "audacious/tuple_formatter.h"
 #include "audacious/output.h"
 #include "wav-sndfile.h"
 
@@ -328,19 +330,15 @@
 	(*title) = get_title(filename);
 }
 
-static TitleInput*
+static Tuple*
 get_song_tuple (gchar *filename)
 {
-	gchar *realfn = NULL; 
-	TitleInput *tuple = bmp_title_input_new();
+	Tuple *ti = tuple_new_from_filename(filename);
+	tuple_associate_string(ti, "codec", "libsndfile");
+	tuple_associate_string(ti, "quality", "lossless");
+	tuple_associate_int(ti, "length", get_song_length(filename));
 
-        realfn = g_filename_from_uri(filename, NULL, NULL);
-        tuple->file_name = g_path_get_basename(realfn ? realfn : filename);
-        tuple->file_path = g_path_get_dirname(realfn ? realfn : filename); 
-	tuple->length = get_song_length(filename);
-        g_free(realfn); realfn = NULL;
-
-	return tuple;
+	return ti;
 }
 
 static void wav_about(void)
--- a/src/wav/wav-sndfile.h	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/wav/wav-sndfile.h	Fri Aug 10 14:23:20 2007 +0300
@@ -31,7 +31,7 @@
 static	void 	play_stop (InputPlayback *playback);
 static	void 	file_seek (InputPlayback *playback, int time);
 static	void 	get_song_info (char *filename, char **title, int *length);
-static	TitleInput* get_song_tuple (gchar *filename);
+static	Tuple*  get_song_tuple (gchar *filename);
 static  void    wav_about (void);
 static	void	play_pause (InputPlayback *playback, gshort p);
 static  void file_mseek (InputPlayback *playback, gulong millisecond);
--- a/src/wav/wav.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/wav/wav.c	Fri Aug 10 14:23:20 2007 +0300
@@ -29,7 +29,9 @@
 #include <string.h>
 
 #include <audacious/util.h>
-#include <audacious/titlestring.h>
+#include <audacious/main.h>
+#include <audacious/tuple.h>
+#include <audacious/tuple_formatter.h>
 #include "audacious/output.h"
 #include <audacious/i18n.h>
 
@@ -204,21 +206,23 @@
 static gchar *
 get_title(const gchar * filename)
 {
-    TitleInput *input;
+    Tuple *tuple;
     gchar *title;
+    gchar *scratch;
 
-    input = bmp_title_input_new();
+    tuple = tuple_new_from_filename(filename);
+
+    tuple_associate_string(tuple, "codec", "RIFF/WAV Audio (ADPCM)");
+    tuple_associate_string(tuple, "quality", "lossless");
 
-    input->file_name = g_path_get_basename(filename);
-    input->file_ext = get_extension(filename);
-    input->file_path = g_path_get_dirname(filename);
+    title = tuple_formatter_process_string(tuple, cfg.gentitle_format);
+    if (*title == '\0')
+    {
+        g_free(title);
+        title = g_strdup(tuple_get_string(tuple, "file-name"));
+    }
 
-    if (!(title = xmms_get_titlestring(xmms_get_gentitle_format(), input)))
-        title = g_strdup(input->file_name);
-
-    g_free(input->file_path);
-    g_free(input->file_name);
-    g_free(input);
+    mowgli_object_unref(tuple);
 
     return title;
 }
--- a/src/wavpack/libwavpack.cxx	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/wavpack/libwavpack.cxx	Fri Aug 10 14:23:20 2007 +0300
@@ -1,3 +1,5 @@
+#include <string>
+
 #include <assert.h>
 #include <string.h>
 #include <stdio.h>
@@ -8,7 +10,7 @@
 #include <audacious/plugin.h>
 #include <audacious/output.h>
 #include <audacious/configdb.h>
-#include <audacious/titlestring.h>
+#include <audacious/main.h>
 #include <audacious/util.h>
 #include <audacious/vfs.h>
 }
@@ -44,7 +46,7 @@
 static bool killDecodeThread;
 static bool AudioError;
 static GThread *thread_handle;
-static TitleInput *wv_get_song_tuple(char *);
+static Tuple *wv_get_song_tuple(char *);
 
 // in ui.cpp
 void wv_configure();
@@ -354,34 +356,43 @@
     return;
 }
 
-static TitleInput *
+static std::string
+WavpackPluginGetQualityString(WavpackContext *ctx)
+{
+    int mode = WavpackGetMode(ctx);
+
+    if (mode & MODE_LOSSLESS)
+        return "lossless";
+
+    if (mode & MODE_HYBRID)
+        return "lossy (hybrid)";
+
+    return "lossy";
+}
+
+static Tuple *
 tuple_from_WavpackContext(const char *fn, WavpackContext *ctx)
 {
     ape_tag tag;
-    TitleInput *ti;
+    Tuple *ti;
     int sample_rate = WavpackGetSampleRate(ctx);
 
-    ti = bmp_title_input_new();
-
-    ti->file_name = g_path_get_basename(fn);
-    ti->file_path = g_path_get_dirname(fn);
-    ti->file_ext = "wv";
+    ti = tuple_new_from_filename(fn);
 
     load_tag(&tag, ctx);
 
-    ti->track_name = g_strdup(tag.title);
-    ti->performer = g_strdup(tag.artist);
-    ti->album_name = g_strdup(tag.album);
-    ti->date = g_strdup(tag.year);
-    ti->track_number = atoi(tag.track);
-    if (ti->track_number < 0)
-        ti->track_number = 0;
-    ti->year = atoi(tag.year);
-    if (ti->year < 0)
-        ti->year = 0;
-    ti->genre = g_strdup(tag.genre);
-    ti->comment = g_strdup(tag.comment);
-    ti->length = (int)(WavpackGetNumSamples(ctx) / sample_rate) * 1000;
+    tuple_associate_string(ti, "title", tag.title);
+    tuple_associate_string(ti, "artist", tag.artist);
+    tuple_associate_string(ti, "album", tag.album);
+    tuple_associate_string(ti, "genre", tag.genre);
+    tuple_associate_string(ti, "comment", tag.comment);
+    tuple_associate_string(ti, "date", tag.year);
+    tuple_associate_string(ti, "quality", WavpackPluginGetQualityString(ctx).c_str());
+    tuple_associate_string(ti, "codec", tag.year);
+
+    tuple_associate_int(ti, "track-number", atoi(tag.track));
+    tuple_associate_int(ti, "year", atoi(tag.year));
+    tuple_associate_int(ti, "length", (int)(WavpackGetNumSamples(ctx) / sample_rate) * 1000);
 
     return ti;
 }
@@ -390,23 +401,23 @@
 generate_title(const char *fn, WavpackContext *ctx)
 {
     static char *displaytitle = NULL;
-    TitleInput *ti;
+    Tuple *ti;
 
     ti = tuple_from_WavpackContext(fn, ctx);
 
-    displaytitle = xmms_get_titlestring(xmms_get_gentitle_format(), ti);
+    displaytitle = tuple_formatter_process_string(ti, cfg.gentitle_format);
     if (!displaytitle || *displaytitle == '\0')
         displaytitle = g_strdup(fn);
 
-    bmp_title_input_free(ti);
+    mowgli_object_unref((void *) ti);
 
     return displaytitle;
 }
 
-static TitleInput *
+static Tuple *
 wv_get_song_tuple(char *filename)
 {
-    TitleInput *ti;
+    Tuple *ti;
     WavpackDecoder d(&mod);
 
     if (!d.attach(filename)) {
--- a/src/wavpack/ui.cxx	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/wavpack/ui.cxx	Fri Aug 10 14:23:20 2007 +0300
@@ -8,7 +8,6 @@
 #include <wavpack/wavpack.h>
 #include <audacious/plugin.h>
 #include <audacious/configdb.h>
-#include <audacious/titlestring.h>
 #include <audacious/util.h>
 #include <audacious/i18n.h>
 }
--- a/src/wma/wma.c	Fri Aug 10 14:23:07 2007 +0300
+++ b/src/wma/wma.c	Fri Aug 10 14:23:20 2007 +0300
@@ -32,13 +32,15 @@
 #include <strings.h>
 #include <glib.h>
 
-#include <audacious/plugin.h>
-#include <audacious/output.h>
-#include <audacious/util.h>
-#include <audacious/titlestring.h>
-#include <audacious/vfs.h>
-#include <audacious/strings.h>
-#include <audacious/i18n.h>
+#include "audacious/plugin.h"
+#include "audacious/output.h"
+#include "audacious/util.h"
+#include "audacious/vfs.h"
+#include "audacious/strings.h"
+#include "audacious/i18n.h"
+#include "audacious/main.h"
+#include "audacious/tuple.h"
+#include "audacious/tuple_formatter.h"
 
 #include "avcodec.h"
 #include "avformat.h"
@@ -82,7 +84,7 @@
 static void wma_seek(InputPlayback *data, int time);
 static void wma_do_pause(InputPlayback *data, short p);
 static void wma_get_song_info(char *filename, char **title, int *length);
-static TitleInput *wma_get_song_tuple(char *filename);
+static Tuple *wma_get_song_tuple(char *filename);
 static char *wsong_title;
 static int wsong_time;
 
@@ -241,79 +243,68 @@
     return ext;
 }
 
-static TitleInput *wma_get_song_tuple(gchar * filename)
+static Tuple *wma_get_song_tuple(gchar * filename)
 {
-    TitleInput *tuple = NULL;
+    Tuple *ti = tuple_new_from_filename(filename);
     AVFormatContext *in = NULL;
-    gchar *filename_proxy = g_strdup(filename);
 
     if (av_open_input_file(&in, str_twenty_to_space(filename), NULL, 0, NULL) < 0)
 	return NULL;
 
-    tuple = bmp_title_input_new();
+    tuple_associate_string(ti, "codec", "Windows Media Audio (WMA)");
+    tuple_associate_string(ti, "quality", "lossy");
 
-    tuple->file_name = g_path_get_basename(filename_proxy);
-    tuple->file_path = g_path_get_dirname(filename_proxy);
-    tuple->file_ext = extname(filename_proxy);
-	
     av_find_stream_info(in);
 
     if(strlen(in->title))
-        tuple->track_name = strdup(in->title);
+        tuple_associate_string(ti, "title", in->title);
     if(strlen(in->author))
-        tuple->performer = strdup(in->author);
+        tuple_associate_string(ti, "artist", in->author);
     if(strlen(in->album))
-        tuple->album_name = strdup(in->album);
+        tuple_associate_string(ti, "album", in->album);
     if(strlen(in->comment))
-        tuple->comment = strdup(in->comment);
+        tuple_associate_string(ti, "comment", in->comment);
     if(strlen(in->genre))
-        tuple->genre = strdup(in->genre);
+        tuple_associate_string(ti, "genre", in->genre);
     if(in->year > 0)
-       tuple->year = in->year;
+        tuple_associate_int(ti, "year", in->year);
     if(in->track > 0)
-        tuple->track_number = in->track;
+        tuple_associate_int(ti, "track", in->track);
     if (in->duration)
-        tuple->length = in->duration / 1000;
+        tuple_associate_int(ti, "length", in->duration / 1000);
 
     av_close_input_file(in);
 
-    return tuple;
+    return ti;
 }
 
 static gchar *get_song_title(AVFormatContext *in, gchar * filename)
 {
     gchar *ret = NULL;
-    TitleInput *input;
+    Tuple *ti = tuple_new_from_filename(filename);
 
-    input = bmp_title_input_new();
-    
+    tuple_associate_string(ti, "codec", "Windows Media Audio (WMA)");
+    tuple_associate_string(ti, "quality", "lossy");
+
     if(strlen(in->title))
-        input->track_name = strdup(in->title);
+        tuple_associate_string(ti, "title", in->title);
     if(strlen(in->author))
-        input->performer = strdup(in->author);
+        tuple_associate_string(ti, "artist", in->author);
     if(strlen(in->album))
-        input->album_name = strdup(in->album);
+        tuple_associate_string(ti, "album", in->album);
     if(strlen(in->comment))
-        input->comment = strdup(in->comment);
+        tuple_associate_string(ti, "comment", in->comment);
     if(strlen(in->genre))
-        input->genre = strdup(in->genre);
+        tuple_associate_string(ti, "genre", in->genre);
     if(in->year > 0)
-       input->year = in->year;
+        tuple_associate_int(ti, "year", in->year);
     if(in->track > 0)
-        input->track_number = in->track;
+        tuple_associate_int(ti, "track", in->track);
+    if (in->duration)
+        tuple_associate_int(ti, "length", in->duration / 1000);
+    
+    ret = tuple_formatter_process_string(ti, cfg.gentitle_format);
 
-    input->file_name = g_path_get_basename(filename);
-    input->file_path = g_path_get_dirname(filename);
-    input->file_ext = extname(filename);
-    ret = xmms_get_titlestring(xmms_get_gentitle_format(), input);
-    if(input) g_free(input);
-
-    if(!ret)
-    {
-	    ret = g_strdup(input->file_name);
-            if (extname(ret) != NULL)
-                    *(extname(ret) - 1) = '\0';
-    }
     return ret;
 }
 
@@ -327,13 +318,13 @@
 
 static void wma_get_song_info(char *filename, char **title_real, int *len_real)
 {
-    TitleInput *tuple = wma_get_song_tuple(filename);
+    Tuple *tuple = wma_get_song_tuple(filename);
 
     if (tuple == NULL)
         return;
 
-    (*len_real) = tuple->length;
-    (*title_real) = xmms_get_titlestring(xmms_get_gentitle_format(), tuple);
+    (*len_real) = tuple_get_int(tuple, "length");
+    (*title_real) = tuple_formatter_process_string(tuple, cfg.gentitle_format);
 }
 
 static void wma_playbuff(InputPlayback *playback, int out_size)