From d41e7f7cec3b4000ffc41d91ef1a612937a04625 Mon Sep 17 00:00:00 2001
From: Jochen Keil <jochen.keil@gmail.com>
Date: Mon, 30 Mar 2009 21:44:41 +0200
Subject: [PATCH 1/6] Build tags from information in cue sheets

With these methods a tag struct can be created
from the cdtext information in a cue sheet.
The methods depend on a cue parsing library.
Reading from strings (char*) as well as from
a file (FILE*) is supported.
---
 src/cue/cue_tag.c | 276 ++++++++++++++++++++++++++++++++++++++++++++++
 src/cue/cue_tag.h |  20 ++++
 2 files changed, 296 insertions(+)
 create mode 100644 src/cue/cue_tag.c
 create mode 100644 src/cue/cue_tag.h

diff --git a/src/cue/cue_tag.c b/src/cue/cue_tag.c
new file mode 100644
index 000000000..ce8202a81
--- /dev/null
+++ b/src/cue/cue_tag.c
@@ -0,0 +1,276 @@
+#include "cue_tag.h"
+
+static struct tag*
+cue_tag_cd(struct Cdtext* cdtext, struct Rem* rem)
+{
+	char* tmp = NULL;
+	struct tag* tag = NULL;
+
+	//if (cdtext == NULL)
+		//return NULL;
+
+	tag = tag_new();
+
+	tag_begin_add(tag);
+
+	{ /* TAG_ITEM_ALBUM_ARTIST */
+	if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_ALBUM_ARTIST, tmp);
+
+	else if ((tmp = cdtext_get(PTI_SONGWRITER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_ALBUM_ARTIST, tmp);
+
+	else if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_ALBUM_ARTIST, tmp);
+
+	else if ((tmp = cdtext_get(PTI_ARRANGER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_ALBUM_ARTIST, tmp);
+	/* TAG_ITEM_ALBUM_ARTIST */ }
+
+	{ /* TAG_ITEM_ARTIST */
+	if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
+
+	else if ((tmp = cdtext_get(PTI_SONGWRITER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
+
+	else if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
+
+	else if ((tmp = cdtext_get(PTI_ARRANGER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
+	/* TAG_ITEM_ARTIST */ }
+
+	/* TAG_ITEM_PERFORMER */
+	if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_PERFORMER, tmp);
+
+	/* TAG_ITEM_COMPOSER */
+	if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_COMPOSER, tmp);
+
+	/* TAG_ITEM_ALBUM */
+	if ((tmp = cdtext_get(PTI_TITLE, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_ALBUM, tmp);
+
+	/* TAG_ITEM_GENRE */
+	if ((tmp = cdtext_get(PTI_GENRE, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_GENRE, tmp);
+
+	/* TAG_ITEM_DATE */
+	if ((tmp = rem_get(REM_DATE, rem)) != NULL)
+		tag_add_item(tag, TAG_ITEM_DATE, tmp);
+
+	/* TAG_ITEM_COMMENT */
+	if ((tmp = cdtext_get(PTI_MESSAGE, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_COMMENT, tmp);
+
+	/* TAG_ITEM_DISC */
+	if ((tmp = cdtext_get(PTI_DISC_ID, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_DISC, tmp);
+
+	/* stream name, usually empty
+	 * tag_add_item(tag, TAG_ITEM_NAME,);
+	 */
+
+	/* REM MUSICBRAINZ entry?
+	tag_add_item(tag, TAG_MUSICBRAINZ_ARTISTID,);
+	tag_add_item(tag, TAG_MUSICBRAINZ_ALBUMID,);
+	tag_add_item(tag, TAG_MUSICBRAINZ_ALBUMARTISTID,);
+	tag_add_item(tag, TAG_MUSICBRAINZ_TRACKID,);
+	*/
+
+	tag_end_add(tag);
+
+	if (tag != NULL)
+	{
+		if (tag_is_empty(tag))
+		{
+			tag_free(tag);
+			return NULL;
+		}
+		else
+			return tag;
+	}
+	else
+		return NULL;
+}
+
+static struct tag*
+cue_tag_track(struct Cdtext* cdtext, struct Rem* rem)
+{
+	char* tmp = NULL;
+	struct tag* tag = NULL;
+
+	//if (cdtext == NULL)
+		//return NULL;
+
+	tag = tag_new();
+
+	tag_begin_add(tag);
+
+	{ /* TAG_ITEM_ARTIST */
+	if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
+
+	else if ((tmp = cdtext_get(PTI_SONGWRITER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
+
+	else if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
+
+	else if ((tmp = cdtext_get(PTI_ARRANGER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
+	/* TAG_ITEM_ARTIST */ }
+
+	/* TAG_ITEM_TITLE */
+	if ((tmp = cdtext_get(PTI_TITLE, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_TITLE, tmp);
+
+	/* TAG_ITEM_GENRE */
+	if ((tmp = cdtext_get(PTI_GENRE, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_GENRE, tmp);
+
+	/* TAG_ITEM_DATE */
+	if ((tmp = rem_get(REM_DATE, rem)) != NULL)
+		tag_add_item(tag, TAG_ITEM_DATE, tmp);
+
+	/* TAG_ITEM_COMPOSER */
+	if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_COMPOSER, tmp);
+
+	/* TAG_ITEM_PERFORMER */
+	if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_PERFORMER, tmp);
+
+	/* TAG_ITEM_COMMENT */
+	if ((tmp = cdtext_get(PTI_MESSAGE, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_COMMENT, tmp);
+
+	/* TAG_ITEM_DISC */
+	if ((tmp = cdtext_get(PTI_DISC_ID, cdtext)) != NULL)
+		tag_add_item(tag, TAG_ITEM_DISC, tmp);
+
+	tag_end_add(tag);
+
+	if (tag != NULL)
+	{
+		if (tag_is_empty(tag))
+		{
+			tag_free(tag);
+			return NULL;
+		}
+		else
+			return tag;
+	}
+	else
+		return NULL;
+}
+
+struct tag*
+cue_tag_file(	FILE* fp,
+		const unsigned int tnum)
+{
+	struct tag* cd_tag = NULL;
+	struct tag* track_tag = NULL;
+	struct tag* merge_tag = NULL;
+	struct Cd* cd = NULL;
+
+	if (tnum > 256)
+		return NULL;
+
+	if (fp == NULL)
+		return NULL;
+	else
+		cd = cue_parse_file(fp);
+
+	if (cd == NULL)
+		return NULL;
+	else
+	{
+		/* tag from CDtext info */
+		cd_tag = cue_tag_cd(	cd_get_cdtext(cd),
+					cd_get_rem(cd));
+
+		/* tag from TRACKtext info */
+		track_tag = cue_tag_track(	track_get_cdtext(	cd_get_track(cd, tnum)),
+						track_get_rem(		cd_get_track(cd, tnum)));
+
+		cd_delete(cd);
+	}
+
+	if ((cd_tag != NULL) && (track_tag != NULL))
+	{
+		merge_tag = tag_merge(cd_tag, track_tag);
+		tag_free(cd_tag);
+		tag_free(track_tag);
+		return merge_tag;
+	}
+
+	else if (cd_tag != NULL)
+	{
+		return cd_tag;
+	}
+
+	else if (track_tag != NULL)
+	{
+		return track_tag;
+	}
+
+	else
+		return NULL;
+}
+
+struct tag*
+cue_tag_string(	char* str,
+		const unsigned int tnum)
+{
+	struct tag* cd_tag = NULL;
+	struct tag* track_tag = NULL;
+	struct tag* merge_tag = NULL;
+	struct Cd* cd = NULL;
+
+	if (tnum > 256)
+		return NULL;
+
+	if (str == NULL)
+		return NULL;
+	else
+		cd = cue_parse_string(str);
+
+	if (cd == NULL)
+		return NULL;
+	else
+	{
+		/* tag from CDtext info */
+		cd_tag = cue_tag_cd(	cd_get_cdtext(cd),
+					cd_get_rem(cd));
+
+		/* tag from TRACKtext info */
+		track_tag = cue_tag_track(	track_get_cdtext(	cd_get_track(cd, tnum)),
+						track_get_rem(		cd_get_track(cd, tnum)));
+
+		cd_delete(cd);
+	}
+
+	if ((cd_tag != NULL) && (track_tag != NULL))
+	{
+		merge_tag = tag_merge(cd_tag, track_tag);
+		tag_free(cd_tag);
+		tag_free(track_tag);
+		return merge_tag;
+	}
+
+	else if (cd_tag != NULL)
+	{
+		return cd_tag;
+	}
+
+	else if (track_tag != NULL)
+	{
+		return track_tag;
+	}
+
+	else
+		return NULL;
+}
diff --git a/src/cue/cue_tag.h b/src/cue/cue_tag.h
new file mode 100644
index 000000000..adc4c466e
--- /dev/null
+++ b/src/cue/cue_tag.h
@@ -0,0 +1,20 @@
+#ifndef MPD_CUE_TAG_H
+#define MPD_CUE_TAG_H
+
+#include "config.h"
+
+#ifdef HAVE_CUE /* libcue */
+
+#include <libcue/libcue.h>
+#include "../tag.h"
+
+struct tag*
+cue_tag_file(	FILE*,
+		const unsigned int);
+
+struct tag*
+cue_tag_string(	char*,
+		const unsigned int);
+
+#endif /* libcue */
+#endif

From 192e29107d874b952b267b3584bf154da3314827 Mon Sep 17 00:00:00 2001
From: Jochen Keil <jochen.keil@gmail.com>
Date: Tue, 31 Mar 2009 21:48:04 +0200
Subject: [PATCH 2/6] Configure/Make dependencies for cue/cue_tag.h/.c

Autoconf/automake support for libcue which
is needed for cue/cue_tag.h/.c.
Libcue will have/has pkg-config support
so this is fairly straightforward.
---
 Makefile.am  | 12 +++++++++---
 configure.ac | 20 ++++++++++++++++++++
 2 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 34e46a87d..c11e8db11 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -152,7 +152,8 @@ mpd_headers = \
 	src/archive_api.h \
 	src/archive_internal.h \
 	src/archive_list.h \
-	src/input/archive_input_plugin.h
+	src/input/archive_input_plugin.h \
+	src/cue/cue_tag.h
 
 src_mpd_SOURCES = \
 	$(mpd_headers) \
@@ -295,7 +296,8 @@ DECODER_CFLAGS = \
 	$(FLUIDSYNTH_CFLAGS) \
 	$(WILDMIDI_CFLAGS) \
 	$(MAD_CFLAGS) \
-	$(FFMPEG_CFLAGS)
+	$(FFMPEG_CFLAGS) \
+	$(CUE_CFLAGS)
 
 DECODER_LIBS = \
 	$(OGGVORBIS_LIBS) $(FLAC_LIBS) \
@@ -306,7 +308,8 @@ DECODER_LIBS = \
 	$(WILDMIDI_LIBS) \
 	$(MAD_LIBS) \
 	$(MP4FF_LIBS) \
-	$(FFMPEG_LIBS)
+	$(FFMPEG_LIBS) \
+	$(CUE_LIBS)
 
 DECODER_SRC = \
 	src/decoder_buffer.c \
@@ -415,6 +418,9 @@ src_mpd_SOURCES += src/zeroconf-bonjour.c
 endif
 endif
 
+if HAVE_CUE
+DECODER_SRC += src/cue/cue_tag.c
+endif
 
 #
 # input plugins
diff --git a/configure.ac b/configure.ac
index b828d3565..1b3fc53da 100644
--- a/configure.ac
+++ b/configure.ac
@@ -169,6 +169,20 @@ dnl ##
 dnl misc libraries
 dnl ##
 
+AC_ARG_ENABLE(cue,
+	AS_HELP_STRING([--enable-cue],
+		[enable support for libcue support]),,
+	enable_cue=auto)
+
+MPD_AUTO_PKG(cue, CUE, [libcue],
+	[libcue parsing library], [libcue not found])
+if test x$enable_cue = xyes; then
+	AC_DEFINE([HAVE_CUE], 1,
+		[Define to enable libcue support])
+fi
+
+AM_CONDITIONAL(HAVE_CUE, test x$enable_cue = xyes)
+
 dnl ##
 dnl Avahi / Zeroconf
 dnl ##
@@ -1391,6 +1405,12 @@ else
 	echo " Zeroconf support ..............disabled"
 fi
 
+if test x$enable_cue = xyes; then
+        echo " libcue support ................enabled"
+else
+        echo " libcue support ................disabled"
+fi
+
 echo ""
 echo "##########################################"
 echo ""

From a1bde6b99d76ac75eddd5a3ac77c77e4249c5de4 Mon Sep 17 00:00:00 2001
From: Jochen Keil <jochen.keil@gmail.com>
Date: Tue, 31 Mar 2009 22:03:08 +0200
Subject: [PATCH 3/6] remove old commented code

---
 src/decoder/flac_plugin.c | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/src/decoder/flac_plugin.c b/src/decoder/flac_plugin.c
index 6d874a886..deb64d285 100644
--- a/src/decoder/flac_plugin.c
+++ b/src/decoder/flac_plugin.c
@@ -778,14 +778,6 @@ flac_filedecode(struct decoder *decoder, const char *fname)
 		flac_container_decode(decoder, fname, false);
 	} else 
 		flac_filedecode_internal(decoder, fname, false);
-
-	/*
-	if (directory->device == CONTAINER)
-	{
-		flac_container_decode(decoder, fname, is_ogg);
-		return;
-	}
-	*/
 }
 
 #endif /* FLAC_API_VERSION_CURRENT >= 7 */

From 6720a0a9404171fba2de86d247c3d7ed0174cbd4 Mon Sep 17 00:00:00 2001
From: Jochen Keil <jochen.keil@gmail.com>
Date: Tue, 31 Mar 2009 22:04:04 +0200
Subject: [PATCH 4/6] free previously allocated flac metadata object

---
 src/decoder/flac_plugin.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/decoder/flac_plugin.c b/src/decoder/flac_plugin.c
index deb64d285..ec1fd01ef 100644
--- a/src/decoder/flac_plugin.c
+++ b/src/decoder/flac_plugin.c
@@ -316,6 +316,7 @@ flac_cue_tag_load(const char *file)
 	if (FLAC__metadata_get_streaminfo(file, si))
 	{
 		sample_rate = si->data.stream_info.sample_rate;
+		FLAC__metadata_object_delete(si);
 	}
 
 	if (FLAC__metadata_get_cuesheet(file, &cs))

From d7b0c768b02f78730830e167e96861b8b059cfa7 Mon Sep 17 00:00:00 2001
From: Jochen Keil <jochen.keil@gmail.com>
Date: Tue, 31 Mar 2009 22:06:28 +0200
Subject: [PATCH 5/6] track length is computed correctly now

---
 src/decoder/flac_plugin.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/decoder/flac_plugin.c b/src/decoder/flac_plugin.c
index ec1fd01ef..603225bfd 100644
--- a/src/decoder/flac_plugin.c
+++ b/src/decoder/flac_plugin.c
@@ -324,10 +324,9 @@ flac_cue_tag_load(const char *file)
 		if (cs->data.cue_sheet.tracks != NULL
 				&& (tnum <= cs->data.cue_sheet.num_tracks - 1))
 		{
-			track_time = cs->data.cue_sheet.tracks[tnum].offset - 1
+			track_time = cs->data.cue_sheet.tracks[tnum].offset
 				- cs->data.cue_sheet.tracks[tnum - 1].offset;
 		}
-
 		FLAC__metadata_object_delete(cs);
 	}
 

From 4bfbdfe5c522011a695c804cfccf190eb4500e9a Mon Sep 17 00:00:00 2001
From: Jochen Keil <jochen.keil@gmail.com>
Date: Tue, 31 Mar 2009 22:07:20 +0200
Subject: [PATCH 6/6] Tag subtracks according to "cuesheet" vorbis comment
 value

Cuesheets are often saved as vorbis comment
flac files (CUESHEET=.. case doesn't matter).
We can parse this now and use the information to
tag the subtracks (from the embedded cuesheets).
---
 src/decoder/flac_plugin.c | 36 ++++++++++++++++++++++++++++++++----
 1 file changed, 32 insertions(+), 4 deletions(-)

diff --git a/src/decoder/flac_plugin.c b/src/decoder/flac_plugin.c
index 603225bfd..e0a2cfe4c 100644
--- a/src/decoder/flac_plugin.c
+++ b/src/decoder/flac_plugin.c
@@ -27,6 +27,10 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#ifdef HAVE_CUE /* libcue */
+#include "../cue/cue_tag.h"
+#endif
+
 /* this code was based on flac123, from flac-tools */
 
 static flac_read_status
@@ -290,20 +294,44 @@ flac_cue_tag_load(const char *file)
 {
 	struct tag* tag = NULL;
 	char* char_tnum = NULL;
-	char* slash = NULL;
+	char* ptr = NULL;
+	unsigned int i = 0;
 	unsigned int tnum = 0;
 	unsigned int sample_rate = 0;
 	FLAC__uint64 track_time = 0;
+#ifdef HAVE_CUE /* libcue */
+	FLAC__StreamMetadata* vc = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
+#endif /* libcue */
 	FLAC__StreamMetadata* si = FLAC__metadata_object_new(FLAC__METADATA_TYPE_STREAMINFO);
 	FLAC__StreamMetadata* cs = FLAC__metadata_object_new(FLAC__METADATA_TYPE_CUESHEET);
 
 	tnum = flac_vtrack_tnum(file);
 	char_tnum = g_strdup_printf("%u", tnum);
 
-	slash = strrchr(file, '/');
-	*slash = '\0';
+	ptr = strrchr(file, '/');
+	*ptr = '\0';
 
-	tag = flac_tag_load(file, char_tnum);
+#ifdef HAVE_CUE /* libcue */
+	if (FLAC__metadata_get_tags(file, &vc))
+	{
+		for (i = 0; i < vc->data.vorbis_comment.num_comments; i++)
+		{
+			if ((ptr = (char*)vc->data.vorbis_comment.comments[i].entry) != NULL)
+			{
+				if (g_ascii_strncasecmp(ptr, "cuesheet", 8) == 0)
+				{
+					while (*(++ptr) != '=');
+					tag = cue_tag_string(   ++ptr,
+								tnum);
+				}
+			}
+		}
+		FLAC__metadata_object_delete(vc);
+	}
+#endif /* libcue */
+
+	if (tag == NULL)
+		tag = flac_tag_load(file, char_tnum);
 
 	if (char_tnum != NULL)
 	{