Index: apps/metadata/mp4.c =================================================================== --- apps/metadata/mp4.c (revision 17486) +++ apps/metadata/mp4.c (working copy) @@ -61,6 +61,10 @@ #define MP4_mp4a MP4_ID('m', 'p', '4', 'a') #define MP4_mp42 MP4_ID('m', 'p', '4', '2') #define MP4_qt MP4_ID('q', 't', ' ', ' ') +#define MP4_soaa MP4_ID('s', 'o', 'a', 'a') +#define MP4_soal MP4_ID('s', 'o', 'a', 'l') +#define MP4_soar MP4_ID('s', 'o', 'a', 'r') +#define MP4_sonm MP4_ID('s', 'o', 'n', 'm') #define MP4_soun MP4_ID('s', 'o', 'u', 'n') #define MP4_stbl MP4_ID('s', 't', 'b', 'l') #define MP4_stsd MP4_ID('s', 't', 's', 'd') @@ -451,6 +455,28 @@ } break; +#ifdef HAVE_TAGCACHE + case MP4_soaa: + read_mp4_tag_string(fd, size, &buffer, &buffer_left, + &id3->sort_albumartist); + break; + + case MP4_soal: + read_mp4_tag_string(fd, size, &buffer, &buffer_left, + &id3->sort_album); + break; + + case MP4_soar: + read_mp4_tag_string(fd, size, &buffer, &buffer_left, + &id3->sort_artist); + break; + + case MP4_sonm: + read_mp4_tag_string(fd, size, &buffer, &buffer_left, + &id3->sort_title); + break; +#endif + case MP4_extra: { char tag_name[TAG_NAME_LENGTH]; Index: apps/metadata/metadata_common.c =================================================================== --- apps/metadata/metadata_common.c (revision 17486) +++ apps/metadata/metadata_common.c (working copy) @@ -291,6 +291,24 @@ { p = &(id3->grouping); } +#ifdef HAVE_TAGCACHE + else if (strcasecmp(name, "artistsort") == 0) + { + p = &(id3->sort_artist); + } + else if (strcasecmp(name, "albumartistsort") == 0) + { + p = &(id3->sort_albumartist); + } + else if (strcasecmp(name, "albumsort") == 0) + { + p = &(id3->sort_album); + } + else if (strcasecmp(name, "titlesort") == 0) + { + p = &(id3->sort_title); + } +#endif else { len = parse_replaygain(name, value, id3, buf, buf_remaining); Index: apps/tagcache.c =================================================================== --- apps/tagcache.c (revision 17486) +++ apps/tagcache.c (working copy) @@ -61,6 +61,7 @@ #include "thread.h" #include "kernel.h" #include "system.h" +#include "debug.h" #include "logf.h" #include "string.h" #include "usb.h" @@ -220,6 +221,7 @@ struct temp_file_entry { long tag_offset[TAG_COUNT]; short tag_length[TAG_COUNT]; + short sorttag_length[TAG_COUNT]; long flag; long data_length; @@ -233,6 +235,7 @@ struct tempbuf_searchidx { long idx_id; char *str; + char *sortstr; int seek; struct tempbuf_id_list idlist; }; @@ -1633,11 +1636,20 @@ return length + 1; } -#define ADD_TAG(entry,tag,data) \ - /* Adding tag */ \ - entry.tag_offset[tag] = offset; \ - entry.tag_length[tag] = check_if_empty(data); \ - offset += entry.tag_length[tag] +static int add_tag(int offset, struct temp_file_entry *entry, int tag, char **data, char **sortdata) +{ + entry->tag_offset[tag] = offset; + entry->tag_length[tag] = check_if_empty(data); + if (sortdata) + { + entry->sorttag_length[tag] = check_if_empty(sortdata); + } + else + { + entry->sorttag_length[tag] = 0; + } + return entry->tag_length[tag] + entry->sorttag_length[tag]; +} static void add_tagcache(char *path, unsigned long mtime #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) @@ -1776,28 +1788,28 @@ has_grouping = id3.grouping != NULL && strlen(id3.grouping) > 0; - ADD_TAG(entry, tag_filename, &path); - ADD_TAG(entry, tag_title, &id3.title); - ADD_TAG(entry, tag_artist, &id3.artist); - ADD_TAG(entry, tag_album, &id3.album); - ADD_TAG(entry, tag_genre, &id3.genre_string); - ADD_TAG(entry, tag_composer, &id3.composer); - ADD_TAG(entry, tag_comment, &id3.comment); + offset += add_tag(offset, &entry, tag_filename, &path, NULL); + offset += add_tag(offset, &entry, tag_title, &id3.title, &id3.sort_title); + offset += add_tag(offset, &entry, tag_artist, &id3.artist, &id3.sort_artist); + offset += add_tag(offset, &entry, tag_album, &id3.album, &id3.sort_album); + offset += add_tag(offset, &entry, tag_genre, &id3.genre_string, NULL); + offset += add_tag(offset, &entry, tag_composer, &id3.composer, NULL); + offset += add_tag(offset, &entry, tag_comment, &id3.comment, NULL); if (has_albumartist) { - ADD_TAG(entry, tag_albumartist, &id3.albumartist); + offset += add_tag(offset, &entry, tag_albumartist, &id3.albumartist, &id3.sort_albumartist); } else { - ADD_TAG(entry, tag_albumartist, &id3.artist); + offset += add_tag(offset, &entry, tag_albumartist, &id3.artist, &id3.sort_artist); } if (has_grouping) { - ADD_TAG(entry, tag_grouping, &id3.grouping); + offset += add_tag(offset, &entry, tag_grouping, &id3.grouping, NULL); } else { - ADD_TAG(entry, tag_grouping, &id3.title); + offset += add_tag(offset, &entry, tag_grouping, &id3.title, NULL); } entry.data_length = offset; @@ -1807,18 +1819,23 @@ /* And tags also... Correct order is critical */ write_item(path); write_item(id3.title); + write_item(id3.sort_title); write_item(id3.artist); + write_item(id3.sort_artist); write_item(id3.album); + write_item(id3.sort_album); write_item(id3.genre_string); write_item(id3.composer); write_item(id3.comment); if (has_albumartist) { write_item(id3.albumartist); + write_item(id3.sort_albumartist); } else { write_item(id3.artist); + write_item(id3.sort_artist); } if (has_grouping) { @@ -1831,10 +1848,11 @@ total_entry_count++; } -static bool tempbuf_insert(char *str, int id, int idx_id, bool unique) +static bool tempbuf_insert(char *str, char *sortstr, int id, int idx_id, bool unique) { struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf; int len = strlen(str)+1; + int sortlen = strlen(sortstr)+1; int i; unsigned crc32; unsigned *crcbuf = (unsigned *)&tempbuf[tempbuf_size-4]; @@ -1897,6 +1915,9 @@ index[tempbufidx].str = &tempbuf[tempbuf_pos]; memcpy(index[tempbufidx].str, str, len); tempbuf_pos += len; + index[tempbufidx].sortstr = &tempbuf[tempbuf_pos]; + memcpy(index[tempbufidx].sortstr, sortstr, len); + tempbuf_pos += sortlen; tempbufidx++; return true; @@ -1908,7 +1929,17 @@ struct tempbuf_searchidx *e1 = (struct tempbuf_searchidx *)p1; struct tempbuf_searchidx *e2 = (struct tempbuf_searchidx *)p2; - + + if (e1->sortstr || e2->sortstr) + { + if (e1->sortstr && e2->sortstr) + return strncasecmp(e1->sortstr, e2->sortstr, TAG_MAXLEN); + else if (e1->sortstr && !e2->sortstr) + return strncasecmp(e1->sortstr, e2->str, TAG_MAXLEN); + else if (!e1->sortstr && e2->sortstr) + return strncasecmp(e1->str, e2->sortstr, TAG_MAXLEN); + } + if (strcmp(e1->str, UNTAGGED) == 0) { if (strcmp(e2->str, UNTAGGED) == 0) @@ -2293,6 +2324,7 @@ struct index_entry idxbuf[IDX_BUF_DEPTH]; int idxbuf_pos; char buf[TAG_MAXLEN+32]; + char sortbuf[TAG_MAXLEN+32]; int fd = -1, masterfd; bool error = false; int init; @@ -2395,8 +2427,8 @@ close(fd); return -2; } - - if (entry.tag_length >= (int)sizeof(buf)) + + if (entry.tag_length >= (int)sizeof(buf)) { logf("too long tag #3"); close(fd); @@ -2419,7 +2451,7 @@ * is saved so we can later reindex the master lookup * table when the index gets resorted. */ - ret = tempbuf_insert(buf, loc/TAGFILE_ENTRY_CHUNK_LENGTH + ret = tempbuf_insert(buf, NULL, loc/TAGFILE_ENTRY_CHUNK_LENGTH + commit_entry_count, entry.idx_id, tagcache_is_unique_tag(index_type)); if (!ret) @@ -2532,6 +2564,8 @@ for (i = 0; i < h->entry_count; i++) { struct temp_file_entry entry; + + DEBUGF("\nOffset #1: %ld\n", lseek(tmpfd, 0, SEEK_CUR)); if (read(tmpfd, &entry, sizeof(struct temp_file_entry)) != sizeof(struct temp_file_entry)) @@ -2541,6 +2575,9 @@ goto error_exit; } + DEBUGF("Offset #2: %ld\n", lseek(tmpfd, 0, SEEK_CUR)); + DEBUGF("Entry count %d\n", i); + /* Read data. */ if (entry.tag_length[index_type] >= (long)sizeof(buf)) { @@ -2550,6 +2587,8 @@ } lseek(tmpfd, entry.tag_offset[index_type], SEEK_CUR); + + DEBUGF("Offset #3: %ld\n", lseek(tmpfd, 0, SEEK_CUR)); if (read(tmpfd, buf, entry.tag_length[index_type]) != entry.tag_length[index_type]) { @@ -2557,11 +2596,36 @@ error = true; goto error_exit; } + + DEBUGF("Offset #4: %ld\n", lseek(tmpfd, 0, SEEK_CUR)); + DEBUGF("Entry length %d\n", entry.tag_length[index_type]); + DEBUGF("Entry offset %ld\n", entry.tag_offset[index_type]); + DEBUGF("Entry data %s\n", buf); + + /* Read sort data. */ + if (entry.sorttag_length[index_type] >= (long)sizeof(sortbuf)) + { + logf("too long sort entry!"); + error = true; + goto error_exit; + } + + DEBUGF("Offset #5: %ld\n", lseek(tmpfd, 0, SEEK_CUR)); + if (read(tmpfd, sortbuf, entry.sorttag_length[index_type]) != + entry.sorttag_length[index_type]) + { + logf("read fail #5"); + error = true; + goto error_exit; + } + + DEBUGF("Sort length %d\n", entry.sorttag_length[index_type]); + DEBUGF("Sort data %s\n", sortbuf); if (tagcache_is_unique_tag(index_type)) - error = !tempbuf_insert(buf, i, -1, true); + error = !tempbuf_insert(buf, sortbuf, i, -1, true); else - error = !tempbuf_insert(buf, i, tcmh.tch.entry_count + i, false); + error = !tempbuf_insert(buf, sortbuf, i, tcmh.tch.entry_count + i, false); if (error) { @@ -2571,7 +2635,8 @@ /* Skip to next. */ lseek(tmpfd, entry.data_length - entry.tag_offset[index_type] - - entry.tag_length[index_type], SEEK_CUR); + entry.tag_length[index_type] - + entry.sorttag_length[index_type], SEEK_CUR); do_timed_yield(); } logf("done"); Index: apps/tagcache.h =================================================================== --- apps/tagcache.h (revision 17486) +++ apps/tagcache.h (working copy) @@ -43,7 +43,7 @@ #define IDX_BUF_DEPTH 64 /* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */ -#define TAGCACHE_MAGIC 0x5443480c +#define TAGCACHE_MAGIC 0x5443480d /* How much to allocate extra space for ramcache. */ #define TAGCACHE_RESERVE 32768 Index: firmware/export/id3.h =================================================================== --- firmware/export/id3.h (revision 17486) +++ firmware/export/id3.h (working copy) @@ -154,6 +154,12 @@ char* comment; char* albumartist; char* grouping; +#ifdef HAVE_TAGCACHE + char* sort_title; + char* sort_album; + char* sort_artist; + char* sort_albumartist; +#endif int discnum; int tracknum; int version; Index: firmware/id3.c =================================================================== --- firmware/id3.c (revision 17486) +++ firmware/id3.c (working copy) @@ -213,7 +213,7 @@ display it, at a runtime cost.) 2. If any special processing beyond copying the tag value from the Id3 - block to the struct mp3entry is rrequired (such as converting to an + block to the struct mp3entry is required (such as converting to an int), write a function to perform this special processing. This function's prototype must match that of @@ -390,7 +390,7 @@ } #if CONFIG_CODEC == SWCODEC -/* parse user defined text, looking for replaygain information. */ +/* Parse the TXXX frame of ID3v2 for various different bits of data */ static int parseuser( struct mp3entry* entry, char* tag, int bufferpos ) { char* value = NULL; @@ -402,8 +402,15 @@ * parse it */ value = tag + desc_len + 1; - value_len = parse_replaygain(tag, value, entry, tag, - bufferpos - (tag - entry->id3v2buf)); + + if (strncmp("replaygain", tag, 10) == 0) { + /* Note: for ID3v2.4, parse_replaygain will not overwrite replaygain + values already parsed from RVA2 tags */ + value_len = parse_replaygain(tag, value, entry, tag, + bufferpos - (tag - entry->id3v2buf)); + } else if (strncmp("ALBUMARTISTSORT", tag, 15) == 0) { + entry->sort_albumartist = value; + } } return tag - entry->id3v2buf + value_len; @@ -499,6 +506,16 @@ { "COMM", 4, offsetof(struct mp3entry, comment), NULL, false }, { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre, false }, { "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre, false }, +#ifdef HAVE_TAGCACHE + /* id3v2.4 versions */ + { "TSOA", 4, offsetof(struct mp3entry, sort_album), NULL, false }, + { "TSOP", 4, offsetof(struct mp3entry, sort_artist), NULL, false }, + { "TSOT", 4, offsetof(struct mp3entry, sort_title), NULL, false }, + /* id3v2.3 versions */ + { "XSOA", 4, offsetof(struct mp3entry, sort_album), NULL, false }, + { "XSOP", 4, offsetof(struct mp3entry, sort_artist), NULL, false }, + { "XSOT", 4, offsetof(struct mp3entry, sort_title), NULL, false }, +#endif #if CONFIG_CODEC == SWCODEC { "TXXX", 4, 0, &parseuser, false }, { "RVA2", 4, 0, &parserva2, true }, @@ -694,7 +711,6 @@ return true; } - /* * Sets the title of an MP3 entry based on its ID3v2 tag. * @@ -760,7 +776,9 @@ } entry->id3version = version; entry->tracknum = entry->year = entry->discnum = 0; - entry->title = entry->artist = entry->album = NULL; /* FIXME incomplete */ + entry->title = entry->artist = entry->album = + entry->sort_title = entry->sort_artist = + entry->sort_album = entry->sort_albumartist = NULL; /* FIXME incomplete */ global_flags = header[5]; @@ -1252,6 +1270,16 @@ entry->albumartist += offset; if (entry->grouping) entry->grouping += offset; +#ifdef HAVE_TAGCACHE + if (entry->sort_artist) + entry->sort_artist += offset; + if (entry->sort_album) + entry->sort_album += offset; + if (entry->sort_title) + entry->sort_title += offset; + if (entry->sort_albumartist) + entry->sort_albumartist += offset; +#endif #if CONFIG_CODEC == SWCODEC if (entry->track_gain_string) entry->track_gain_string += offset;