summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-03-30 19:51:20 +0200
committerGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-03-30 19:51:20 +0200
commitdb6aa2c38cbc931de32438e3085994e1fd6eb0e4 (patch)
treeb4c3dcf7cf573989bfd625bd53a7b94545365a87
parent38b5a92feebd5501e97d4f285f25d8e9bc6bba69 (diff)
id3v2 conversions code WIP
-rw-r--r--deadbeef.h2
-rw-r--r--junklib.c222
-rw-r--r--junklib.h6
-rw-r--r--plugins.c2
-rw-r--r--plugins/gtkui/trkproperties.c19
5 files changed, 249 insertions, 2 deletions
diff --git a/deadbeef.h b/deadbeef.h
index e531aa3c..f1a5a4e2 100644
--- a/deadbeef.h
+++ b/deadbeef.h
@@ -410,9 +410,11 @@ typedef struct {
int (*junk_read_id3v2) (DB_playItem_t *it, DB_FILE *fp);
int (*junk_read_id3v2_full) (DB_playItem_t *it, DB_id3v2_tag_t *tag, DB_FILE *fp);
int (*junk_id3v2_convert_24_to_23) (DB_id3v2_tag_t *tag24, DB_id3v2_tag_t *tag23);
+ int (*junk_id3v2_convert_23_to_24) (DB_id3v2_tag_t *tag23, DB_id3v2_tag_t *tag24);
void (*junk_free_id3v2) (DB_id3v2_tag_t *tag);
int (*junk_write_id3v2) (const char *fname, DB_id3v2_tag_t *tag);
DB_id3v2_frame_t *(*junk_id3v2_add_text_frame_23) (DB_id3v2_tag_t *tag, const char *frame_id, const char *value);
+ DB_id3v2_frame_t *(*junk_id3v2_add_text_frame_24) (DB_id3v2_tag_t *tag, const char *frame_id, const char *value);
int (*junk_id3v2_remove_frames) (DB_id3v2_tag_t *tag, const char *frame_id);
int (*junk_read_ape) (DB_playItem_t *it, DB_FILE *fp);
int (*junk_get_leading_size) (DB_FILE *fp);
diff --git a/junklib.c b/junklib.c
index 95bba29e..24b403aa 100644
--- a/junklib.c
+++ b/junklib.c
@@ -919,7 +919,37 @@ junk_id3v2_add_text_frame_23 (DB_id3v2_tag_t *tag, const char *frame_id, const c
return f;
}
+DB_id3v2_frame_t *
+junk_id3v2_add_text_frame_24 (DB_id3v2_tag_t *tag, const char *frame_id, const char *value) {
+ trace ("junklib: setting 2.4 text frame '%s' = '%s'\n", frame_id, value);
+
+ // make a frame
+ int outlen = strlen (value);
+ int size = outlen + 1 + 1;
+ trace ("calculated frame size = %d\n", size);
+ DB_id3v2_frame_t *f = malloc (size + sizeof (DB_id3v2_frame_t));
+ memset (f, 0, sizeof (DB_id3v2_frame_t));
+ strcpy (f->id, frame_id);
+ // flags are all zero
+ f->size = size;
+ f->data[0] = 3; // encoding=utf8
+ memcpy (&f->data[1], value, outlen);
+ f->data[outlen+1] = 0;
+// if (encoding == 1) { // we don't write ucs2
+// f->data[outlen+2] = 0;
+// }
+ // append to tag
+ DB_id3v2_frame_t *tail;
+ for (tail = tag->frames; tail && tail->next; tail = tail->next);
+ if (tail) {
+ tail->next = f;
+ }
+ else {
+ tag->frames = f;
+ }
+ return f;
+}
// [+] TODO: remove all unsync bytes during conversion, where appropriate
// TODO: some non-Txxx frames might still need charset conversion
// TODO: 2.4 TDTG frame (tagging time) should not be converted, but might be useful to create it
@@ -965,7 +995,7 @@ junk_id3v2_convert_24_to_23 (DB_id3v2_tag_t *tag24, DB_id3v2_tag_t *tag23) {
DB_id3v2_frame_t *f23 = NULL;
// we are altering the tag, so check for tag alter preservation
if (tag24->flags & (1<<6)) {
- continue; // discard the tag
+ continue; // discard the frame
}
int simplecopy = 0; // means format is the same in 2.3 and 2.4
@@ -1128,6 +1158,196 @@ junk_id3v2_convert_24_to_23 (DB_id3v2_tag_t *tag24, DB_id3v2_tag_t *tag23) {
}
int
+junk_id3v2_convert_23_to_24 (DB_id3v2_tag_t *tag23, DB_id3v2_tag_t *tag24) {
+ DB_id3v2_frame_t *f23;
+ DB_id3v2_frame_t *tail = NULL;
+
+ const char *copy_frames[] = {
+ "AENC", "APIC",
+ "COMM", "COMR", "ENCR",
+ "ETCO", "GEOB", "GRID",
+ "LINK", "MCDI", "MLLT", "OWNE", "PRIV",
+ "POPM", "POSS", "RBUF",
+ "RVRB",
+ "SYLT", "SYTC",
+ "UFID", "USER", "USLT",
+ NULL
+ };
+
+ // NOTE: all Wxxx frames are copy_frames, handled as special case
+
+ // "TDRC" TDAT with conversion from ID3v2-strct timestamp to DDMM format
+ // "TDOR" TORY with conversion from ID3v2-strct timestamp to year
+ // TODO: "TIPL" IPLS with conversion to non-text format
+
+ const char *text_frames[] = {
+ "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT", "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED", "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE", "TXXX", "TDRC", NULL
+ };
+
+ for (f23 = tag23->frames; f23; f23 = f23->next) {
+ // we are altering the tag, so check for tag alter preservation
+ if (tag23->flags & (1<<7)) {
+ continue; // discard the frame
+ }
+
+ int simplecopy = 0; // means format is the same in 2.3 and 2.4
+ int text = 0; // means this is a text frame
+
+ int i;
+
+ if (f23->id[0] == 'W') { // covers all W000..WZZZ tags
+ simplecopy = 1;
+ }
+
+ if (!simplecopy) {
+ for (i = 0; copy_frames[i]; i++) {
+ if (!strcmp (f23->id, copy_frames[i])) {
+ simplecopy = 1;
+ break;
+ }
+ }
+ }
+
+ if (!simplecopy) {
+ // check if this is a text frame
+ for (i = 0; text_frames[i]; i++) {
+ if (!strcmp (f23->id, text_frames[i])) {
+ text = 1;
+ break;
+ }
+ }
+ }
+
+
+ if (!simplecopy && !text) {
+ continue; // unknown frame
+ }
+
+ // convert flags
+ uint8_t flags[2];
+ // 1st byte (status flags) is the same, but shifted by 1 bit to the
+ // right
+ flags[0] = f23->flags[0] >> 1;
+
+ // 2nd byte (format flags) is quite different
+ // 2.4 format is %0h00kmnp (grouping, compression, encryption, unsync)
+ // 2.3 format is %ijk00000 (compression, encryption, grouping)
+ flags[1] = 0;
+ if (f23->flags[1] & (1 << 4)) {
+ flags[1] |= (1 << 6);
+ }
+ if (f23->flags[1] & (1 << 7)) {
+ flags[1] |= (1 << 3);
+ }
+ if (f23->flags[1] & (1 << 6)) {
+ flags[1] |= (1 << 2);
+ }
+ if (f23->flags[1] & (1 << 5)) {
+ flags[1] |= (1 << 1);
+ }
+
+ DB_id3v2_frame_t *f24 = NULL;
+ if (simplecopy) {
+ f24 = malloc (sizeof (DB_id3v2_frame_t) + f23->size);
+ memset (f24, 0, sizeof (DB_id3v2_frame_t) + f23->size);
+ strcpy (f24->id, f23->id);
+ f24->size = f23->size;
+ memcpy (f24->data, f23->data, f23->size);
+ f24->flags[0] = flags[0];
+ f24->flags[1] = flags[1];
+ }
+ else if (text) {
+ // decode text into utf8
+ char str[f23->size+2];
+
+ int unsync = 0;
+ if (tag23->flags & (1<<7)) {
+ unsync = 1;
+ }
+ if (f23->flags[1] & 1) {
+ unsync = 1;
+ }
+ id3v2_string_read (4, str, f23->size, unsync, f23->data);
+ char *decoded = convstr_id3v2_4 (str, f23->size);
+ if (!decoded) {
+ trace ("junk_id3v2_convert_23_to_24: failed to decode text frame %s\n", f23->id);
+ continue; // failed, discard it
+ }
+ if (!strcmp (f23->id, "TDRC")) {
+ trace ("junk_id3v2_convert_23_to_24: TDRC text: %s\n", decoded);
+ int year, month, day;
+ int c = sscanf (decoded, "%4d-%2d-%2d", &year, &month, &day);
+ if (c >= 1) {
+ char s[5];
+ snprintf (s, sizeof (s), "%04d", year);
+ f24 = junk_id3v2_add_text_frame_24 (tag24, "TYER", s);
+ if (f24) {
+ tail = f24;
+ f24 = NULL;
+ }
+ }
+ if (c == 3) {
+ char s[5];
+ snprintf (s, sizeof (s), "%02d%02d", month, day);
+ f24 = junk_id3v2_add_text_frame_24 (tag24, "TDAT", s);
+ if (f24) {
+ tail = f24;
+ f24 = NULL;
+ }
+ }
+ else {
+ trace ("junk_id3v2_add_text_frame_24: 2.4 TDRC doesn't have month/day info; discarded\n");
+ }
+ }
+ else if (!strcmp (f23->id, "TORY")) {
+ trace ("junk_id3v2_convert_23_to_24: TDOR text: %s\n", decoded);
+ int year;
+ int c = sscanf (decoded, "%4d", &year);
+ if (c == 1) {
+ char s[5];
+ snprintf (s, sizeof (s), "%04d", &year);
+// FIXME f24 = junk_id3v2_add_text_frame_24 (tag24, "TDOR", s);
+ if (f24) {
+ tail = f24;
+ f24 = NULL;
+ }
+ }
+ else {
+ trace ("junk_id3v2_add_text_frame_24: 2.4 TDOR doesn't have month/day info; discarded\n");
+ }
+ }
+ else {
+ // encode for 2.3
+ f24 = junk_id3v2_add_text_frame_24 (tag24, f23->id, decoded);
+ if (f24) {
+ tail = f24;
+ f24 = NULL;
+ }
+ }
+ free (decoded);
+ }
+ if (f24) {
+ if (tail) {
+ tail->next = f24;
+ }
+ else {
+ tag24->frames = f24;
+ }
+ tail = f24;
+ }
+ }
+
+ // convert tag header
+ tag24->version[0] = 4;
+ tag24->version[1] = 0;
+ tag24->flags = tag23->flags;
+ tag24->flags &= ~(1<<4); // no footer (unsupported in 2.3)
+ tag24->flags &= ~(1<<7); // no unsync
+
+ return 0;
+}
+
+int
junk_write_id3v2 (const char *fname, DB_id3v2_tag_t *tag) {
/*
steps:
diff --git a/junklib.h b/junklib.h
index 9ef335cf..9bc07165 100644
--- a/junklib.h
+++ b/junklib.h
@@ -32,9 +32,15 @@ junk_read_id3v2_full (struct playItem_s *it, DB_id3v2_tag_t *tag, DB_FILE *fp);
int
junk_id3v2_convert_24_to_23 (DB_id3v2_tag_t *tag24, DB_id3v2_tag_t *tag23);
+int
+junk_id3v2_convert_23_to_24 (DB_id3v2_tag_t *tag23, DB_id3v2_tag_t *tag24);
+
DB_id3v2_frame_t *
junk_id3v2_add_text_frame_23 (DB_id3v2_tag_t *tag, const char *frame_id, const char *value);
+DB_id3v2_frame_t *
+junk_id3v2_add_text_frame_24 (DB_id3v2_tag_t *tag, const char *frame_id, const char *value);
+
int
junk_id3v2_remove_frames (DB_id3v2_tag_t *tag, const char *frame_id);
diff --git a/plugins.c b/plugins.c
index 50c714d6..82899bc6 100644
--- a/plugins.c
+++ b/plugins.c
@@ -182,10 +182,12 @@ static DB_functions_t deadbeef_api = {
.junk_read_id3v2 = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_read_id3v2,
.junk_read_id3v2_full = (int (*)(DB_playItem_t *, DB_id3v2_tag_t *tag, DB_FILE *fp))junk_read_id3v2_full,
.junk_id3v2_convert_24_to_23 = junk_id3v2_convert_24_to_23,
+ .junk_id3v2_convert_23_to_24 = junk_id3v2_convert_23_to_24,
.junk_free_id3v2 = junk_free_id3v2,
.junk_write_id3v2 = junk_write_id3v2,
.junk_id3v2_remove_frames = junk_id3v2_remove_frames,
.junk_id3v2_add_text_frame_23 = junk_id3v2_add_text_frame_23,
+ .junk_id3v2_add_text_frame_24 = junk_id3v2_add_text_frame_24,
.junk_read_ape = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_read_ape,
.junk_get_leading_size = junk_get_leading_size,
.junk_get_leading_size_stdio = junk_get_leading_size_stdio,
diff --git a/plugins/gtkui/trkproperties.c b/plugins/gtkui/trkproperties.c
index dfc02906..15148049 100644
--- a/plugins/gtkui/trkproperties.c
+++ b/plugins/gtkui/trkproperties.c
@@ -239,9 +239,26 @@ on_write_tags_clicked (GtkButton *button,
goto error;
}
if (deadbeef->junk_write_id3v2 (track->fname, &tag23) < 0) {
- fprintf (stderr, "failed to write tags to %s\n", track->fname);
+ fprintf (stderr, "failed to write 2.3 tag to %s\n", track->fname);
+ deadbeef->junk_free_id3v2 (&tag23);
+ goto error;
+ }
+ deadbeef->junk_free_id3v2 (&tag23);
+ }
+ else if (tag.version[0] == 3) {
+ DB_id3v2_tag_t tag24;
+ memset (&tag24, 0, sizeof (tag24));
+ int res = deadbeef->junk_id3v2_convert_23_to_24 (&tag, &tag24);
+ if (res == -1) {
+ deadbeef->junk_free_id3v2 (&tag24);
+ goto error;
+ }
+ if (deadbeef->junk_write_id3v2 (track->fname, &tag24) < 0) {
+ fprintf (stderr, "failed to write 2.4 tag to %s\n", track->fname);
+ deadbeef->junk_free_id3v2 (&tag24);
goto error;
}
+ deadbeef->junk_free_id3v2 (&tag24);
}
}