Support for input plugins with multiple tracks per file

Olivier Galibert <[email protected]>
Newsgroups gmane.comp.multimedia.xmms.devel
Message-ID <[email protected]>
Everything is in the subject.

Single-track-per-channel plugins are source and binary compatible with
the post-patch version.  For multi-track plugins the InputPlugin
structure has 3 more optional fields placed at the end:

int (*count_tracks) (char *filename);	/* Return the number of tracks available in a given file */
void (*play_track) (char *filename, int track);	/* Play a given track from a file */
void (*get_track_info) (char *filename, int track, char **title, int *length);	/* Function to grab the title string */

play_track replaces play_file (which MUST be null, that's how the type
is recognized) and get_track_info replaces get_song_info.

Playlists are compatible both ways, the only difference is when the
track is non-zero the "filename" line in .m3u files is changed into
"trackno filename" with a space for separator.

I attach the patch to xmms-cvs and also a patch to xmms-sidplay-0.40
as an example of use.

  OG.

_______________________________________________
xmms-devel mailing list
[email protected]
http://lists.xmms.org/mailman/listinfo/xmms-devel
multitrack.diff (text/plain, 11.2 KB)
Index: xmms/input.c
===================================================================
RCS file: /cvs/xmms/xmms/input.c,v
retrieving revision 1.18
diff -u -r1.18 input.c
--- xmms/input.c	13 Jul 2003 22:16:39 -0000	1.18
+++ xmms/input.c	17 Nov 2003 01:28:31 -0000
@@ -224,7 +224,7 @@
 	pthread_mutex_unlock(&vis_mutex);
 }
 
-gboolean input_check_file(gchar * filename)
+guint input_check_file(gchar * filename)
 {
 	GList *node;
 	InputPlugin *ip;
@@ -235,13 +235,20 @@
 		ip = (InputPlugin *) node->data;
 		if (ip && !g_list_find(disabled_iplugins, ip) &&
 		    ip->is_our_file(filename))
-			return TRUE;
+		{
+			/* Single-track plugin */
+			if (ip->play_file)
+				return 1;
+			/* Multi-track */
+			else
+				return ip->count_tracks(filename);
+		}
 		node = node->next;
 	}
-	return FALSE;
+	return 0;
 }
 
-void input_play(char *filename)
+void input_play(char *filename, guint track)
 {
 	GList *node;
 	InputPlugin *ip;
@@ -263,7 +270,10 @@
 		{
 			set_current_input_plugin(ip);
 			ip->output = get_current_output_plugin();
-			ip->play_file(filename);
+			if (ip->play_file)
+				ip->play_file(filename);
+			else
+				ip->play_track(filename, track);
 			ip_data->playing = TRUE;
 			return;
 
@@ -339,11 +349,13 @@
 			get_current_input_plugin()->set_eq(on, preamp, bands);
 }
 
-void input_get_song_info(gchar * filename, gchar ** title, gint * length)
+void input_get_song_info(gchar * filename, guint track, gchar ** title, gint * length)
 {
 	GList *node;
 	InputPlugin *ip = NULL;
 
+	fprintf(stderr, "GSI %s:%d\n", filename, track);
+
 	node = get_input_list();
 	while (node)
 	{
@@ -353,8 +365,13 @@
 			break;
 		node = node->next;
 	}
-	if (ip && node && ip->get_song_info)
-		ip->get_song_info(filename, title, length);
+	if (ip && node && (ip->play_file ? ip->get_song_info != 0 : ip->get_track_info != 0))
+	{
+		if (ip->play_file)
+			ip->get_song_info(filename, title, length);
+		else
+			ip->get_track_info(filename, track, title, length);
+	}
 	else
 	{
 		gchar *temp, *ext;
Index: xmms/input.h
===================================================================
RCS file: /cvs/xmms/xmms/input.h,v
retrieving revision 1.4
diff -u -r1.4 input.h
--- xmms/input.h	16 Feb 2000 21:05:57 -0000	1.4
+++ xmms/input.h	17 Nov 2003 01:28:31 -0000
@@ -31,14 +31,14 @@
 InputPlugin *get_current_input_plugin(void);
 void set_current_input_plugin(InputPlugin * ip);
 InputVisType input_get_vis_type();
-gboolean input_check_file(gchar * filename);
-void input_play(char *filename);
+guint input_check_file(gchar * filename);
+void input_play(char *filename, guint track);
 void input_stop(void);
 void input_pause(void);
 int input_get_time(void);
 void input_set_eq(int on, float preamp, float *bands);
 void input_seek(int time);
-void input_get_song_info(gchar * filename, gchar ** title, gint * length);
+void input_get_song_info(gchar * filename, guint track, gchar ** title, gint * length);
 gboolean get_input_playing(void);
 gboolean get_input_paused(void);
 guchar *input_get_vis(gint time);
Index: xmms/playlist.c
===================================================================
RCS file: /cvs/xmms/xmms/playlist.c,v
retrieving revision 1.78
diff -u -r1.78 playlist.c
--- xmms/playlist.c	6 Nov 2003 23:36:35 -0000	1.78
+++ xmms/playlist.c	17 Nov 2003 01:28:32 -0000
@@ -254,12 +254,13 @@
 	}
 }
 
-static void __playlist_ins_with_info(char *filename, long pos, char* title, int len)
+static void __playlist_ins_with_info(char *filename, long pos, guint track, char* title, int len)
 {
 	PlaylistEntry *entry;
 
 	entry = g_malloc0(sizeof (PlaylistEntry));
 	entry->filename = g_strdup(filename);
+	entry->track = track;
 	if (title)
 		entry->title = g_strdup(title);
 	entry->length = len;
@@ -271,9 +272,11 @@
 	playlist_get_info_scan_active = TRUE;
 }
 
-static void __playlist_ins(char * filename, long pos)
+static void __playlist_ins(char * filename, long pos, guint count)
 {
-	__playlist_ins_with_info(filename, pos, NULL, -1);
+	guint i;
+	for (i=0; i<count; i++)
+		__playlist_ins_with_info(filename, pos, i, NULL, -1);
 }
 
 static gboolean is_playlist_name(char *pathname)
@@ -288,13 +291,16 @@
 }
 
 
-void playlist_ins(gchar * filename, glong pos)
+guint playlist_ins(gchar * filename, glong pos)
 {
 	gboolean ispl = FALSE;
+	guint count = 0;
 
 	ispl = is_playlist_name (filename);
+	if (!ispl)
+		count = input_check_file(filename);
 
-	if (!ispl && !input_check_file(filename))
+	if (!ispl && !count)
 	{
 		/*
 		 * Some files (typically produced by some cgi-scripts)
@@ -333,14 +339,16 @@
 	}
 
 	if (ispl)
-		playlist_load_ins(filename, pos);
+		return playlist_load_ins(filename, pos);
 	else
-		if (input_check_file(filename))
+		if (count)
 		{
-			__playlist_ins(filename, pos);
+			__playlist_ins(filename, pos, count);
 			playlist_generate_shuffle_list();
 			playlistwin_update_list();
-		}
+			return count;
+		} else
+			return 0;
 }
 
 static guint devino_hash(gconstpointer key)
@@ -361,6 +369,12 @@
 	return TRUE;
 }
 
+struct __playlist_file_entry
+{
+	char *filename;
+	guint track_count;
+};
+
 static GList* playlist_dir_find_files(char *path, gboolean background, GHashTable *htab)
 {
 	DIR *dir;
@@ -399,8 +413,10 @@
 		node = ilist;
 		while (node)
 		{
-			char *name = g_strconcat(temp, node->data, NULL);
-			list = g_list_prepend(list, name);
+			struct __playlist_file_entry *e = g_malloc (sizeof (*e));
+			e->filename = g_strconcat(temp, node->data, NULL);
+			e->track_count = 1;
+			list = g_list_prepend(list, e);
 			g_free(node->data);
 			node = g_list_next(node);
 		}
@@ -438,10 +454,19 @@
 			g_free(filename);
 			list = g_list_concat(list, sub);
 		}
-		else if (input_check_file(filename))
-			list = g_list_prepend(list, filename);
 		else
-			g_free(filename);
+		{
+			guint track_count = input_check_file(filename);
+			if (track_count)
+			{
+				struct __playlist_file_entry *e = g_malloc (sizeof (*e));
+				e->filename = filename;
+				e->track_count = track_count;
+				list = g_list_prepend(list, e);
+			}
+			else
+				g_free(filename);
+		}
 
 		while (background && gtk_events_pending())
 			gtk_main_iteration();
@@ -468,11 +493,13 @@
 	node = list;
 	while (node)
 	{
-		__playlist_ins(node->data, pos);
-		entries++;
+		struct __playlist_file_entry *e = node->data;
+		__playlist_ins(e->filename, pos, e->track_count);
+		entries += e->track_count;
 		if (pos >= 0)
-			pos++;
-		g_free(node->data);
+			pos += e->track_count;
+		g_free(e->filename);
+		g_free(e);
 		node = g_list_next(node);
 	}
 	g_list_free(list);
@@ -510,10 +537,7 @@
 			if (is_playlist_name (string))
 				i = playlist_load_ins(string, pos);
 			else
-			{
-				playlist_ins(string, pos);
-				i = 1;
-			}
+				i = playlist_ins(string, pos);
 		}
 		entries += i;
 		if (pos >= 0)
@@ -531,6 +555,7 @@
 void playlist_play(void)
 {
 	char *filename = NULL;
+	guint track = 0;
 
 	if (get_playlist_length() == 0)
 		return;
@@ -580,13 +605,14 @@
 				playlist_position = playlist->data;
 		}
 		filename = playlist_position->filename;
+		track    = playlist_position->track;
 	}
 	PL_UNLOCK();
 
 	if (!filename)
 		return;
 	
-	input_play(filename);
+	input_play(filename, track);
 
 	if (input_get_time() != -1)
 	{
@@ -1041,7 +1067,10 @@
 				fprintf(file, "#EXTINF:%d,%s\n",
 					seconds, entry->title);
 			}
-			fprintf(file, "%s\n", entry->filename);
+			if (entry->track)
+				fprintf(file, "%d %s\n", entry->track, entry->filename);
+			else
+				fprintf(file, "%s\n", entry->filename);
 		}
 		node = g_list_next(node);
 	}
@@ -1056,10 +1085,21 @@
 
 static void playlist_load_ins_file(char *filename, char *playlist_name, long pos, char *title, int len)
 {
-	char *temp, *path;
+	char *temp, *path, *p;
+	guint track = 0;
 
 	filename = g_strstrip(filename);
 
+	p = filename;
+	while (*p && (*p >= '0' && *p <= '9'))
+		p++;
+
+	if(*p == ' ' && p != filename)
+	{
+		track = strtol(filename, 0, 10);
+		filename = p + 1;
+	}
+
 	if (cfg.use_backslash_as_dir_delimiter)
 	{
 		while ((temp = strchr(filename, '\\')) != NULL)
@@ -1074,16 +1114,16 @@
 			*temp = '\0';
 		else
 		{
-			__playlist_ins_with_info(filename, pos, title, len);
+			__playlist_ins_with_info(filename, pos, track, title, len);
 			return;
 		}
 		temp = g_strdup_printf("%s/%s", path, filename);
-		__playlist_ins_with_info(temp, pos, title, len);
+		__playlist_ins_with_info(temp, pos, track, title, len);
 		g_free(temp);
 		g_free(path);
 	}
 	else
-		__playlist_ins_with_info(filename, pos, title, len);
+		__playlist_ins_with_info(filename, pos, track, title, len);
 }
 
 static void parse_extm3u_info(char *info, char **title, int *length)
@@ -1739,14 +1779,16 @@
 	 */
 	char *temp_filename, *temp_title;
 	int temp_length;
+	guint temp_track;
 
 	temp_filename = g_strdup(entry->filename);
+	temp_track = entry->track;
 	temp_title = NULL;
 	temp_length = -1;
 
 	/* We don't want to lock the playlist while reading info */
 	PL_UNLOCK();
-	input_get_song_info(temp_filename, &temp_title, &temp_length);
+	input_get_song_info(temp_filename, temp_track, &temp_title, &temp_length);
 	PL_LOCK();
 	g_free(temp_filename);
 
Index: xmms/playlist.h
===================================================================
RCS file: /cvs/xmms/xmms/playlist.h,v
retrieving revision 1.19
diff -u -r1.19 playlist.h
--- xmms/playlist.h	8 Jun 2003 21:52:02 -0000	1.19
+++ xmms/playlist.h	17 Nov 2003 01:28:32 -0000
@@ -23,6 +23,7 @@
 typedef struct
 {
 	gchar *filename;
+	guint track;
 	gchar *title;
 	gint length;
 	gboolean selected;
@@ -33,7 +34,7 @@
 void playlist_delete(gboolean crop);
 /*  void playlist_add(gchar * filename); */
 #define playlist_add(filename) playlist_ins(filename, -1)
-void playlist_ins(gchar * filename, glong pos);
+guint playlist_ins(gchar * filename, glong pos);
 /*  void playlist_add_dir(gchar * dir); */
 #define playlist_add_dir(directory) playlist_ins_dir(directory, -1, TRUE)
 guint playlist_ins_dir(char *dir, long pos, gboolean background);
Index: xmms/plugin.h
===================================================================
RCS file: /cvs/xmms/xmms/plugin.h,v
retrieving revision 1.6
diff -u -r1.6 plugin.h
--- xmms/plugin.h	5 Jul 2001 22:35:01 -0000	1.6
+++ xmms/plugin.h	17 Nov 2003 01:28:33 -0000
@@ -92,7 +92,7 @@
 	int (*is_our_file) (char *filename);	/* Return 1 if the plugin can handle the file */
 	GList *(*scan_dir) (char *dirname);	/* Look in Input/cdaudio/cdaudio.c to see how */
 	/* to use this */
-	void (*play_file) (char *filename);	/* Guess what... */
+	void (*play_file) (char *filename);	/* Play file for single-track per file plugins only */
 	void (*stop) (void);	/* Tricky one */
 	void (*pause) (short paused);	/* Pause or unpause */
 	void (*seek) (int time);	/* Seek to the specified time */
@@ -112,6 +112,12 @@
 	void (*get_song_info) (char *filename, char **title, int *length);	/* Function to grab the title string */
 	void (*file_info_box) (char *filename);		/* Bring up an info window for the filename passed in */
 	OutputPlugin *output;	/* Handle to the current output plugin. Filled in by xmms */
+
+	/* Starting from here the fields are for multi-track plugins */
+
+	int (*count_tracks) (char *filename);	/* Return the number of tracks available in a given file */
+	void (*play_track) (char *filename, int track);	/* Play a given track from a file */
+	void (*get_track_info) (char *filename, int track, char **title, int *length);	/* Function to grab the title string */
 }
 InputPlugin;
sidplay.diff (text/plain, 4.1 KB)
--- xmms-sidplay-0.40.orig/sid.cc	2001-06-10 22:27:25.000000000 +0200
+++ xmms-sidplay-0.40/sid.cc	2003-11-17 02:27:08.706675677 +0100
@@ -17,12 +17,12 @@
 static void sid_about(void);
 static void sid_configure(void);
 static int sid_is_our_file(char *filename);
-static void sid_play_file(char *filename);
+static int sid_count_tracks(char *filename);
+static void sid_play_track(char *filename, int track);
 static void sid_stop(void);
 static void sid_pause(short paused);
-static void sid_seek(int time);
 static int sid_get_time(void);
-static void sid_get_song_info(char *filename,char **title,int *length);
+static void sid_get_track_info(char *filename,int track,char **title,int *length);
 static void sid_file_info_box(char *filename);
 
 InputPlugin sid_ip = {
@@ -34,10 +34,10 @@
   sid_configure,
   sid_is_our_file,
   NULL,
-  sid_play_file,
+  NULL,
   sid_stop,
   sid_pause,
-  sid_seek,
+  NULL,
   NULL,
   sid_get_time,
   NULL,
@@ -47,9 +47,12 @@
   NULL,
   NULL,
   NULL,
-  sid_get_song_info,
+  NULL,
   sid_file_info_box,
-  NULL
+  NULL,
+  sid_count_tracks,
+  sid_play_track,
+  sid_get_track_info
 };
 
 emuConfig config = {
@@ -139,14 +142,14 @@
   if (info.songs == 0)
     return 0;
 
-  sid_ip.set_info(g_strdup_printf("%s (%i/%i)", info.nameString, currentSong, info.songs), 
-		  100000, 
+  sid_ip.set_info(g_strdup_printf("%s (%i/%i)", info.nameString, currentSong+1, info.songs), 
+		  -1, 
 		  config.channels * (config.bitsPerSample / 8) * config.frequency, 
 		  config.frequency, config.channels);
 
   if (infolabel) {
     gtk_label_set_text(GTK_LABEL(infolabel), 
-		       g_strdup_printf("%i/%i", currentSong, info.songs));
+		       g_strdup_printf("%i/%i", currentSong+1, info.songs));
   }
 
   if(!sid_ip.output->
@@ -752,8 +755,20 @@
     gtk_widget_destroy(dialog);
 }
 
+static int
+sid_count_tracks(char *filename)
+{
+  sidTuneInfo info;
+
+  if(!tune->open(filename))
+    return 0;
+
+  tune->getInfo(info);
+  return info.songs;
+}
+
 static void
-sid_play_file(char *filename)
+sid_play_track(char *filename, int track)
 {
   sidTuneInfo info;
 
@@ -768,7 +783,7 @@
     sid_file_info_box(filename);
 
   tune->getInfo(info);
-  change_current_song(info.startSong);
+  change_current_song(track);
 }
 
 static void
@@ -787,20 +802,6 @@
   sid_ip.output->pause(paused);
 }
 
-static void
-sid_seek(int time)
-{
-  sidTuneInfo info;
-  tune->getInfo(info);
-
-  if (time == 0 && currentSong > 1) {
-    change_current_song(currentSong - 1);
-  }
-  else if (time == 100 && currentSong < info.songs) {
-    change_current_song(currentSong + 1);
-  }
-}
-
 static int 
 sid_get_time(void)
 {
@@ -811,21 +812,15 @@
 }
 
 static void
-sid_get_song_info(char *filename,char **title,int *length)
+sid_get_track_info(char *filename,int track,char **title,int *length)
 {
   sidTune ttune(filename);
   sidTuneInfo info;
 
   ttune.getInfo(info);
-  *title = g_strdup_printf("%s (%i/%i)", info.nameString, info.startSong, info.songs);
-
-  *length = 0;
-}
+  *title = g_strdup_printf("%s (%i/%i)", info.nameString, track+1, info.songs);
 
-static void
-tune_changed_cb(GtkWidget *widget, gpointer data)
-{
-  change_current_song((int)GTK_ADJUSTMENT(widget)->value);
+  *length = -1;
 }
 
 static void
@@ -877,17 +872,9 @@
   label = gtk_label_new(text);
   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 2);
 
-  infolabel = gtk_label_new(g_strdup_printf("%i/%i", currentSong, info.songs));
+  infolabel = gtk_label_new(g_strdup_printf("%i/%i", currentSong+1, info.songs));
   gtk_box_pack_start(GTK_BOX(vbox), infolabel, FALSE, TRUE, 2);
 
-  adjustment = gtk_adjustment_new(currentSong, 1.0, info.songs + 1, 
-				  1.0, 1.0, 1.0);
-  currentSongBar = gtk_hscrollbar_new(GTK_ADJUSTMENT(adjustment));
-  gtk_range_set_update_policy(GTK_RANGE(currentSongBar), GTK_UPDATE_CONTINUOUS);
-  gtk_signal_connect(GTK_OBJECT(adjustment), "value_changed",
-		     GTK_SIGNAL_FUNC(tune_changed_cb), NULL);
-  gtk_box_pack_start(GTK_BOX(vbox), currentSongBar, TRUE, FALSE, 2);
-
   button = gtk_button_new_with_label("Ok");
   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(infodialog)->action_area), button, 
 		     TRUE, TRUE, 5);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.