aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am26
-rw-r--r--src/song.c1
-rw-r--r--src/tag.c120
-rw-r--r--src/tag.h2
-rw-r--r--src/tag_ape.c145
-rw-r--r--src/tag_ape.h31
-rw-r--r--test/read_tags.c159
8 files changed, 362 insertions, 123 deletions
diff --git a/.gitignore b/.gitignore
index 7f9b5fbc5..43aaf5bbb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,4 +41,5 @@ doc/protocol
doc/api
test/software_volume
test/run_decoder
+test/read_tags
test/run_encoder
diff --git a/Makefile.am b/Makefile.am
index 7316a6539..63029b58f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -120,6 +120,7 @@ mpd_headers = \
src/tag.h \
src/tag_internal.h \
src/tag_pool.h \
+ src/tag_ape.h \
src/tag_id3.h \
src/tag_print.h \
src/tag_save.h \
@@ -209,6 +210,7 @@ src_mpd_SOURCES = \
src/tag_pool.c \
src/tag_print.c \
src/tag_save.c \
+ src/tag_ape.c \
src/strset.c \
src/uri.c \
src/utils.c \
@@ -509,6 +511,7 @@ if ENABLE_TEST
noinst_PROGRAMS = \
test/run_decoder \
+ test/read_tags \
test/run_encoder \
test/software_volume
@@ -520,7 +523,22 @@ test_run_decoder_LDADD = $(MPD_LIBS) \
$(INPUT_LIBS) $(DECODER_LIBS)
test_run_decoder_SOURCES = test/run_decoder.c \
src/conf.c src/buffer2array.c src/utils.c src/log.c \
- src/tag.c src/tag_pool.c src/tag_id3.c \
+ src/tag.c src/tag_pool.c \
+ src/replay_gain.c \
+ src/uri.c \
+ $(ARCHIVE_SRC) \
+ $(INPUT_SRC) \
+ $(DECODER_SRC)
+
+test_read_tags_CPPFLAGS = $(AM_CPPFLAGS) \
+ $(ID3TAG_CFLAGS) \
+ $(INPUT_CFLAGS) $(DECODER_CFLAGS)
+test_read_tags_LDADD = $(MPD_LIBS) \
+ $(ID3TAG_LIBS) \
+ $(INPUT_LIBS) $(DECODER_LIBS)
+test_read_tags_SOURCES = test/read_tags.c \
+ src/conf.c src/buffer2array.c src/utils.c src/log.c \
+ src/tag.c src/tag_pool.c src/tag_ape.c \
src/replay_gain.c \
src/uri.c \
$(ARCHIVE_SRC) \
@@ -538,6 +556,12 @@ test_run_encoder_LDADD = $(MPD_LIBS) \
test_software_volume_SOURCES = test/software_volume.c \
src/audio_parser.c \
src/pcm_volume.c
+
+if HAVE_ID3TAG
+test_run_decoder_SOURCES += src/tag_id3.c
+test_read_tags_SOURCES += src/tag_id3.c
+endif
+
endif
diff --git a/src/song.c b/src/song.c
index 0262d4b1a..823e71fc7 100644
--- a/src/song.c
+++ b/src/song.c
@@ -22,6 +22,7 @@
#include "mapper.h"
#include "decoder_list.h"
#include "decoder_plugin.h"
+#include "tag_ape.h"
#include "tag_id3.h"
#include "tag.h"
diff --git a/src/tag.c b/src/tag.c
index 960dd4b17..e4aa6bf0e 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -117,126 +117,6 @@ void tag_lib_init(void)
g_free(temp);
}
-struct tag *tag_ape_load(const char *file)
-{
- struct tag *ret = NULL;
- FILE *fp;
- int tagCount;
- char *buffer = NULL;
- char *p;
- size_t tagLen;
- size_t size;
- unsigned long flags;
- int i;
- char *key;
-
- struct {
- unsigned char id[8];
- uint32_t version;
- uint32_t length;
- uint32_t tagCount;
- unsigned char flags[4];
- unsigned char reserved[8];
- } footer;
-
- const char *apeItems[7] = {
- "title",
- "artist",
- "album",
- "comment",
- "genre",
- "track",
- "year"
- };
-
- int tagItems[7] = {
- TAG_ITEM_TITLE,
- TAG_ITEM_ARTIST,
- TAG_ITEM_ALBUM,
- TAG_ITEM_COMMENT,
- TAG_ITEM_GENRE,
- TAG_ITEM_TRACK,
- TAG_ITEM_DATE,
- };
-
- fp = fopen(file, "r");
- if (!fp)
- return NULL;
-
- /* determine if file has an apeV2 tag */
- if (fseek(fp, 0, SEEK_END))
- goto fail;
- size = (size_t)ftell(fp);
- if (fseek(fp, size - sizeof(footer), SEEK_SET))
- goto fail;
- if (fread(&footer, 1, sizeof(footer), fp) != sizeof(footer))
- goto fail;
- if (memcmp(footer.id, "APETAGEX", sizeof(footer.id)) != 0)
- goto fail;
- if (GUINT32_FROM_LE(footer.version) != 2000)
- goto fail;
-
- /* find beginning of ape tag */
- tagLen = GUINT32_FROM_LE(footer.length);
- if (tagLen < sizeof(footer))
- goto fail;
- if (fseek(fp, size - tagLen, SEEK_SET))
- goto fail;
-
- /* read tag into buffer */
- tagLen -= sizeof(footer);
- if (tagLen <= 0)
- goto fail;
- buffer = g_malloc(tagLen);
- if (fread(buffer, 1, tagLen, fp) != tagLen)
- goto fail;
-
- /* read tags */
- tagCount = GUINT32_FROM_LE(footer.tagCount);
- p = buffer;
- while (tagCount-- && tagLen > 10) {
- size = GUINT32_FROM_LE(*(const uint32_t *)p);
- p += 4;
- tagLen -= 4;
- flags = GUINT32_FROM_LE(*(const uint32_t *)p);
- p += 4;
- tagLen -= 4;
-
- /* get the key */
- key = p;
- while (tagLen - size > 0 && *p != '\0') {
- p++;
- tagLen--;
- }
- p++;
- tagLen--;
-
- /* get the value */
- if (tagLen < size)
- goto fail;
-
- /* we only care about utf-8 text tags */
- if (!(flags & (0x3 << 1))) {
- for (i = 0; i < 7; i++) {
- if (strcasecmp(key, apeItems[i]) == 0) {
- if (!ret)
- ret = tag_new();
- tag_add_item_n(ret, tagItems[i],
- p, size);
- }
- }
- }
- p += size;
- tagLen -= size;
- }
-
-fail:
- if (fp)
- fclose(fp);
- g_free(buffer);
- return ret;
-}
-
struct tag *tag_new(void)
{
struct tag *ret = g_new(struct tag, 1);
diff --git a/src/tag.h b/src/tag.h
index 9f1549be4..026ded5c4 100644
--- a/src/tag.h
+++ b/src/tag.h
@@ -61,8 +61,6 @@ struct tag {
unsigned num_items;
};
-struct tag *tag_ape_load(const char *file);
-
struct tag *tag_new(void);
void tag_lib_init(void);
diff --git a/src/tag_ape.c b/src/tag_ape.c
new file mode 100644
index 000000000..c38b07a55
--- /dev/null
+++ b/src/tag_ape.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2003-2009 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tag_ape.h"
+#include "tag.h"
+
+#include <glib.h>
+
+#include <stdio.h>
+
+struct tag *
+tag_ape_load(const char *file)
+{
+ struct tag *ret = NULL;
+ FILE *fp;
+ int tagCount;
+ char *buffer = NULL;
+ char *p;
+ size_t tagLen;
+ size_t size;
+ unsigned long flags;
+ int i;
+ char *key;
+
+ struct {
+ unsigned char id[8];
+ uint32_t version;
+ uint32_t length;
+ uint32_t tagCount;
+ unsigned char flags[4];
+ unsigned char reserved[8];
+ } footer;
+
+ const char *apeItems[7] = {
+ "title",
+ "artist",
+ "album",
+ "comment",
+ "genre",
+ "track",
+ "year"
+ };
+
+ int tagItems[7] = {
+ TAG_ITEM_TITLE,
+ TAG_ITEM_ARTIST,
+ TAG_ITEM_ALBUM,
+ TAG_ITEM_COMMENT,
+ TAG_ITEM_GENRE,
+ TAG_ITEM_TRACK,
+ TAG_ITEM_DATE,
+ };
+
+ fp = fopen(file, "r");
+ if (!fp)
+ return NULL;
+
+ /* determine if file has an apeV2 tag */
+ if (fseek(fp, 0, SEEK_END))
+ goto fail;
+ size = (size_t)ftell(fp);
+ if (fseek(fp, size - sizeof(footer), SEEK_SET))
+ goto fail;
+ if (fread(&footer, 1, sizeof(footer), fp) != sizeof(footer))
+ goto fail;
+ if (memcmp(footer.id, "APETAGEX", sizeof(footer.id)) != 0)
+ goto fail;
+ if (GUINT32_FROM_LE(footer.version) != 2000)
+ goto fail;
+
+ /* find beginning of ape tag */
+ tagLen = GUINT32_FROM_LE(footer.length);
+ if (tagLen < sizeof(footer))
+ goto fail;
+ if (fseek(fp, size - tagLen, SEEK_SET))
+ goto fail;
+
+ /* read tag into buffer */
+ tagLen -= sizeof(footer);
+ if (tagLen <= 0)
+ goto fail;
+ buffer = g_malloc(tagLen);
+ if (fread(buffer, 1, tagLen, fp) != tagLen)
+ goto fail;
+
+ /* read tags */
+ tagCount = GUINT32_FROM_LE(footer.tagCount);
+ p = buffer;
+ while (tagCount-- && tagLen > 10) {
+ size = GUINT32_FROM_LE(*(const uint32_t *)p);
+ p += 4;
+ tagLen -= 4;
+ flags = GUINT32_FROM_LE(*(const uint32_t *)p);
+ p += 4;
+ tagLen -= 4;
+
+ /* get the key */
+ key = p;
+ while (tagLen - size > 0 && *p != '\0') {
+ p++;
+ tagLen--;
+ }
+ p++;
+ tagLen--;
+
+ /* get the value */
+ if (tagLen < size)
+ goto fail;
+
+ /* we only care about utf-8 text tags */
+ if (!(flags & (0x3 << 1))) {
+ for (i = 0; i < 7; i++) {
+ if (strcasecmp(key, apeItems[i]) == 0) {
+ if (!ret)
+ ret = tag_new();
+ tag_add_item_n(ret, tagItems[i],
+ p, size);
+ }
+ }
+ }
+ p += size;
+ tagLen -= size;
+ }
+
+fail:
+ if (fp)
+ fclose(fp);
+ g_free(buffer);
+ return ret;
+}
diff --git a/src/tag_ape.h b/src/tag_ape.h
new file mode 100644
index 000000000..abd10d631
--- /dev/null
+++ b/src/tag_ape.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2003-2009 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MPD_TAG_APE_H
+#define MPD_TAG_APE_H
+
+/**
+ * Loads the APE tag from a file.
+ *
+ * @param path_fs the path of the file in filesystem encoding
+ * @return a tag object, or NULL if the file has no APE tag
+ */
+struct tag *
+tag_ape_load(const char *path_fs);
+
+#endif
diff --git a/test/read_tags.c b/test/read_tags.c
new file mode 100644
index 000000000..cdee3722f
--- /dev/null
+++ b/test/read_tags.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2003-2009 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "decoder_list.h"
+#include "decoder_api.h"
+#include "input_stream.h"
+#include "audio_format.h"
+#include "pcm_volume.h"
+#include "tag_ape.h"
+#include "tag_id3.h"
+
+#include <glib.h>
+
+#include <assert.h>
+#include <unistd.h>
+
+/**
+ * No-op dummy.
+ */
+void
+pcm_volume(G_GNUC_UNUSED void *buffer, G_GNUC_UNUSED int length,
+ G_GNUC_UNUSED const struct audio_format *format,
+ G_GNUC_UNUSED int volume)
+{
+}
+
+void
+decoder_initialized(G_GNUC_UNUSED struct decoder *decoder,
+ G_GNUC_UNUSED const struct audio_format *audio_format,
+ G_GNUC_UNUSED bool seekable,
+ G_GNUC_UNUSED float total_time)
+{
+}
+
+char *decoder_get_uri(G_GNUC_UNUSED struct decoder *decoder)
+{
+ return NULL;
+}
+
+enum decoder_command
+decoder_get_command(G_GNUC_UNUSED struct decoder *decoder)
+{
+ return DECODE_COMMAND_NONE;
+}
+
+void decoder_command_finished(G_GNUC_UNUSED struct decoder *decoder)
+{
+}
+
+double decoder_seek_where(G_GNUC_UNUSED struct decoder *decoder)
+{
+ return 1.0;
+}
+
+void decoder_seek_error(G_GNUC_UNUSED struct decoder *decoder)
+{
+}
+
+size_t
+decoder_read(G_GNUC_UNUSED struct decoder *decoder,
+ struct input_stream *is,
+ void *buffer, size_t length)
+{
+ return input_stream_read(is, buffer, length);
+}
+
+enum decoder_command
+decoder_data(G_GNUC_UNUSED struct decoder *decoder,
+ G_GNUC_UNUSED struct input_stream *is,
+ const void *data, size_t datalen,
+ G_GNUC_UNUSED float data_time, G_GNUC_UNUSED uint16_t bit_rate,
+ G_GNUC_UNUSED struct replay_gain_info *replay_gain_info)
+{
+ write(1, data, datalen);
+ return DECODE_COMMAND_NONE;
+}
+
+enum decoder_command
+decoder_tag(G_GNUC_UNUSED struct decoder *decoder,
+ G_GNUC_UNUSED struct input_stream *is,
+ G_GNUC_UNUSED const struct tag *tag)
+{
+ return DECODE_COMMAND_NONE;
+}
+
+static void
+print_tag(const struct tag *tag)
+{
+ if (tag->time >= 0)
+ g_print("time=%d\n", tag->time);
+
+ for (unsigned i = 0; i < tag->num_items; ++i)
+ g_print("%s=%s\n",
+ tag_item_names[tag->items[i]->type],
+ tag->items[i]->value);
+}
+
+int main(int argc, char **argv)
+{
+ const char *decoder_name, *path;
+ const struct decoder_plugin *plugin;
+ struct tag *tag;
+ bool empty;
+
+ if (argc != 3) {
+ g_printerr("Usage: read_tags DECODER FILE\n");
+ return 1;
+ }
+
+ decoder_name = argv[1];
+ path = argv[2];
+
+ decoder_plugin_init_all();
+
+ plugin = decoder_plugin_from_name(decoder_name);
+ if (plugin == NULL) {
+ g_printerr("No such decoder: %s\n", decoder_name);
+ return 1;
+ }
+
+ tag = decoder_plugin_tag_dup(plugin, path);
+ decoder_plugin_deinit_all();
+ if (tag == NULL) {
+ g_printerr("Failed to read tags\n");
+ return 1;
+ }
+
+ print_tag(tag);
+
+ empty = tag_is_empty(tag);
+ tag_free(tag);
+
+ if (empty) {
+ tag = tag_ape_load(path);
+ if (tag == NULL)
+ tag = tag_id3_load(path);
+ if (tag != NULL) {
+ print_tag(tag);
+ tag_free(tag);
+ }
+ }
+
+ return 0;
+}