diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c
index 82bf7541f2..3c74509ec8 100644
--- a/apps/gui/gwps-common.c
+++ b/apps/gui/gwps-common.c
@@ -866,6 +866,9 @@ static char *get_token_value(struct gui_wps *gwps,
case WPS_TOKEN_METADATA_ALBUM_ARTIST:
return id3->albumartist;
+ case WPS_TOKEN_METADATA_GROUPING:
+ return id3->grouping;
+
case WPS_TOKEN_METADATA_GENRE:
return id3->genre_string;
diff --git a/apps/gui/gwps.h b/apps/gui/gwps.h
index b02b87f2c9..83ff14b80b 100644
--- a/apps/gui/gwps.h
+++ b/apps/gui/gwps.h
@@ -191,6 +191,7 @@ enum wps_token_type {
WPS_TOKEN_METADATA_ARTIST,
WPS_TOKEN_METADATA_COMPOSER,
WPS_TOKEN_METADATA_ALBUM_ARTIST,
+ WPS_TOKEN_METADATA_GROUPING,
WPS_TOKEN_METADATA_ALBUM,
WPS_TOKEN_METADATA_GENRE,
WPS_TOKEN_METADATA_DISC_NUMBER,
diff --git a/apps/gui/wps_debug.c b/apps/gui/wps_debug.c
index 50ffcce119..58e6ab49aa 100644
--- a/apps/gui/wps_debug.c
+++ b/apps/gui/wps_debug.c
@@ -289,6 +289,11 @@ static void dump_wps_tokens(struct wps_data *data)
next_str(next));
break;
+ case WPS_TOKEN_METADATA_GROUPING:
+ snprintf(buf, sizeof(buf), "%strack grouping",
+ next_str(next));
+ break;
+
case WPS_TOKEN_METADATA_GENRE:
snprintf(buf, sizeof(buf), "%strack genre",
next_str(next));
diff --git a/apps/gui/wps_parser.c b/apps/gui/wps_parser.c
index d66e47dfe7..8471bff7d8 100644
--- a/apps/gui/wps_parser.c
+++ b/apps/gui/wps_parser.c
@@ -200,6 +200,7 @@ static const struct wps_tag all_tags[] = {
{ WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
{ WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
{ WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
+ { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
{ WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
{ WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
{ WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
@@ -213,6 +214,7 @@ static const struct wps_tag all_tags[] = {
{ WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_DYNAMIC, NULL },
{ WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_DYNAMIC, NULL },
{ WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_DYNAMIC, NULL },
+ { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_DYNAMIC, NULL },
{ WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_DYNAMIC, NULL },
{ WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_DYNAMIC, NULL },
{ WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_DYNAMIC, NULL },
diff --git a/apps/lang/deutsch.lang b/apps/lang/deutsch.lang
index a50d021c4e..51ff6e454f 100644
--- a/apps/lang/deutsch.lang
+++ b/apps/lang/deutsch.lang
@@ -11017,3 +11017,17 @@
*: "Als Aufnahmeverzeichnis festlegen"
+
+ id: LANG_ID3_GROUPING
+ desc: in tag viewer
+ user:
+
+
+ *: "[Werk]"
+
+
+ *: ""
+
+
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 6f631282f7..4fd4ecf54d 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -11004,3 +11004,17 @@
*: "Demos"
+
+ id: LANG_ID3_GROUPING
+ desc: in tag viewer
+ user:
+
+
+ *: "[Work]"
+
+
+ *: ""
+
+
diff --git a/apps/metadata/metadata_common.c b/apps/metadata/metadata_common.c
index d1a1087188..e3579473f0 100644
--- a/apps/metadata/metadata_common.c
+++ b/apps/metadata/metadata_common.c
@@ -280,6 +280,18 @@ long parse_tag(const char* name, char* value, struct mp3entry* id3,
{
p = &(id3->albumartist);
}
+ else if (strcasecmp(name, "grouping") == 0)
+ {
+ p = &(id3->grouping);
+ }
+ else if (strcasecmp(name, "content group") == 0)
+ {
+ p = &(id3->grouping);
+ }
+ else if (strcasecmp(name, "contentgroup") == 0)
+ {
+ p = &(id3->grouping);
+ }
else
{
len = parse_replaygain(name, value, id3, buf, buf_remaining);
diff --git a/apps/metadata/mp4.c b/apps/metadata/mp4.c
index aecb4cf17f..8162cd987f 100644
--- a/apps/metadata/mp4.c
+++ b/apps/metadata/mp4.c
@@ -38,6 +38,7 @@
#define MP4_alac MP4_ID('a', 'l', 'a', 'c')
#define MP4_calb MP4_ID(0xa9, 'a', 'l', 'b')
#define MP4_cART MP4_ID(0xa9, 'A', 'R', 'T')
+#define MP4_cgrp MP4_ID(0xa9, 'g', 'r', 'p')
#define MP4_cgen MP4_ID(0xa9, 'g', 'e', 'n')
#define MP4_cnam MP4_ID(0xa9, 'n', 'a', 'm')
#define MP4_cwrt MP4_ID(0xa9, 'w', 'r', 't')
@@ -378,6 +379,11 @@ static bool read_mp4_tags(int fd, struct mp3entry* id3,
&id3->albumartist);
break;
+ case MP4_cgrp:
+ read_mp4_tag_string(fd, size, &buffer, &buffer_left,
+ &id3->grouping);
+ break;
+
case MP4_calb:
read_mp4_tag_string(fd, size, &buffer, &buffer_left,
&id3->album);
diff --git a/apps/screens.c b/apps/screens.c
index 1f0939ab8d..b74347405e 100644
--- a/apps/screens.c
+++ b/apps/screens.c
@@ -1151,6 +1151,7 @@ static const int id3_headers[]=
LANG_ID3_ARTIST,
LANG_ID3_ALBUM,
LANG_ID3_ALBUMARTIST,
+ LANG_ID3_GROUPING,
LANG_ID3_DISCNUM,
LANG_ID3_TRACKNUM,
LANG_ID3_COMMENT,
@@ -1193,7 +1194,10 @@ static char * id3_get_info(int selected_item, void* data, char *buffer)
case 3:/*LANG_ID3_ALBUMARTIST*/
info=id3->albumartist;
break;
- case 4:/*LANG_ID3_DISCNUM*/
+ case 4:/*LANG_ID3_ALBUMARTIST*/
+ info=id3->grouping;
+ break;
+ case 5:/*LANG_ID3_DISCNUM*/
if (id3->disc_string)
info = id3->disc_string;
else if (id3->discnum)
@@ -1202,7 +1206,7 @@ static char * id3_get_info(int selected_item, void* data, char *buffer)
info = buffer;
}
break;
- case 5:/*LANG_ID3_TRACKNUM*/
+ case 6:/*LANG_ID3_TRACKNUM*/
if (id3->track_string)
info = id3->track_string;
else if (id3->tracknum)
@@ -1211,13 +1215,13 @@ static char * id3_get_info(int selected_item, void* data, char *buffer)
info = buffer;
}
break;
- case 6:/*LANG_ID3_COMMENT*/
+ case 7:/*LANG_ID3_COMMENT*/
info=id3->comment;
break;
- case 7:/*LANG_ID3_GENRE*/
+ case 8:/*LANG_ID3_GENRE*/
info = id3->genre_string;
break;
- case 8:/*LANG_ID3_YEAR*/
+ case 9:/*LANG_ID3_YEAR*/
if (id3->year_string)
info = id3->year_string;
else if (id3->year)
@@ -1226,34 +1230,34 @@ static char * id3_get_info(int selected_item, void* data, char *buffer)
info = buffer;
}
break;
- case 9:/*LANG_ID3_LENGTH*/
+ case 10:/*LANG_ID3_LENGTH*/
format_time(buffer, MAX_PATH, id3->length);
info=buffer;
break;
- case 10:/*LANG_ID3_PLAYLIST*/
+ case 11:/*LANG_ID3_PLAYLIST*/
snprintf(buffer, MAX_PATH, "%d/%d", playlist_get_display_index(),
playlist_amount());
info=buffer;
break;
- case 11:/*LANG_ID3_BITRATE*/
+ case 12:/*LANG_ID3_BITRATE*/
snprintf(buffer, MAX_PATH, "%d kbps%s", id3->bitrate,
id3->vbr ? str(LANG_ID3_VBR) : (const unsigned char*) "");
info=buffer;
break;
- case 12:/*LANG_ID3_FREQUENCY*/
+ case 13:/*LANG_ID3_FREQUENCY*/
snprintf(buffer, MAX_PATH, "%ld Hz", id3->frequency);
info=buffer;
break;
#if CONFIG_CODEC == SWCODEC
- case 13:/*LANG_ID3_TRACK_GAIN*/
+ case 14:/*LANG_ID3_TRACK_GAIN*/
info=id3->track_gain_string;
break;
- case 14:/*LANG_ID3_ALBUM_GAIN*/
+ case 15:/*LANG_ID3_ALBUM_GAIN*/
info=id3->album_gain_string;
break;
- case 15:/*LANG_ID3_PATH*/
+ case 16:/*LANG_ID3_PATH*/
#else
- case 13:/*LANG_ID3_PATH*/
+ case 17:/*LANG_ID3_PATH*/
#endif
info=id3->path;
break;
diff --git a/apps/tagcache.c b/apps/tagcache.c
index da51e0c4e9..309718a932 100644
--- a/apps/tagcache.c
+++ b/apps/tagcache.c
@@ -108,11 +108,11 @@ static long tempbuf_pos;
/* Tags we want to get sorted (loaded to the tempbuf). */
static const int sorted_tags[] = { tag_artist, tag_album, tag_genre,
- tag_composer, tag_comment, tag_albumartist, tag_title };
+ tag_composer, tag_comment, tag_albumartist, tag_grouping, tag_title };
/* Uniqued tags (we can use these tags with filters and conditional clauses). */
static const int unique_tags[] = { tag_artist, tag_album, tag_genre,
- tag_composer, tag_comment, tag_albumartist };
+ tag_composer, tag_comment, tag_albumartist, tag_grouping };
/* Numeric tags (we can use these tags with conditional clauses). */
static const int numeric_tags[] = { tag_year, tag_discnumber, tag_tracknumber, tag_length,
@@ -123,7 +123,7 @@ static const int numeric_tags[] = { tag_year, tag_discnumber, tag_tracknumber, t
/* String presentation of the tags defined in tagcache.h. Must be in correct order! */
static const char *tags_str[] = { "artist", "album", "genre", "title",
- "filename", "composer", "comment", "albumartist", "year", "discnumber", "tracknumber",
+ "filename", "composer", "comment", "albumartist", "grouping", "year", "discnumber", "tracknumber",
"bitrate", "length", "playcount", "rating", "playtime", "lastplayed", "commitid" };
/* Status information of the tagcache. */
@@ -188,7 +188,7 @@ struct master_header {
/* For the endianess correction */
static const char *tagfile_entry_ec = "ss";
-static const char *index_entry_ec = "lllllllllllllllllll"; /* (1 + TAG_COUNT) * l */
+static const char *index_entry_ec = "llllllllllllllllllll"; /* (1 + TAG_COUNT) * l */
static const char *tagcache_header_ec = "lll";
static const char *master_header_ec = "llllll";
@@ -1549,6 +1549,7 @@ bool tagcache_fill_tags(struct mp3entry *id3, const char *filename)
id3->composer = get_tag_string(entry, tag_composer);
id3->comment = get_tag_string(entry, tag_comment);
id3->albumartist = get_tag_string(entry, tag_albumartist);
+ id3->grouping = get_tag_string(entry, tag_grouping);
id3->playcount = get_tag_numeric(entry, tag_playcount);
id3->rating = get_tag_numeric(entry, tag_rating);
@@ -1615,6 +1616,7 @@ static void add_tagcache(char *path)
int offset = 0;
int path_length = strlen(path);
bool has_albumartist;
+ bool has_grouping;
if (cachefd < 0)
return ;
@@ -1708,6 +1710,8 @@ static void add_tagcache(char *path)
/* String tags. */
has_albumartist = track.id3.albumartist != NULL
&& strlen(track.id3.albumartist) > 0;
+ has_grouping = track.id3.grouping != NULL
+ && strlen(track.id3.grouping) > 0;
ADD_TAG(entry, tag_filename, &path);
ADD_TAG(entry, tag_title, &track.id3.title);
@@ -1724,6 +1728,14 @@ static void add_tagcache(char *path)
{
ADD_TAG(entry, tag_albumartist, &track.id3.artist);
}
+ if (has_grouping)
+ {
+ ADD_TAG(entry, tag_grouping, &track.id3.grouping);
+ }
+ else
+ {
+ ADD_TAG(entry, tag_grouping, &track.id3.title);
+ }
entry.data_length = offset;
/* Write the header */
@@ -1745,6 +1757,14 @@ static void add_tagcache(char *path)
{
write_item(track.id3.artist);
}
+ if (has_grouping)
+ {
+ write_item(track.id3.grouping);
+ }
+ else
+ {
+ write_item(track.id3.title);
+ }
total_entry_count++;
}
diff --git a/apps/tagcache.h b/apps/tagcache.h
index ada624783a..118b4c4119 100644
--- a/apps/tagcache.h
+++ b/apps/tagcache.h
@@ -23,7 +23,7 @@
#include "id3.h"
enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
- tag_filename, tag_composer, tag_comment, tag_albumartist, tag_year,
+ tag_filename, tag_composer, tag_comment, tag_albumartist, tag_grouping, tag_year,
tag_discnumber, tag_tracknumber, tag_bitrate, tag_length, tag_playcount, tag_rating,
tag_playtime, tag_lastplayed, tag_commitid,
/* Virtual tags */
@@ -31,7 +31,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
tag_virt_playtime_min, tag_virt_playtime_sec,
tag_virt_entryage, tag_virt_autoscore };
-#define TAG_COUNT 18
+#define TAG_COUNT 19
/* Maximum length of a single tag. */
#define TAG_MAXLEN (MAX_PATH*2)
@@ -43,7 +43,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
#define IDX_BUF_DEPTH 64
/* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */
-#define TAGCACHE_MAGIC 0x5443480A
+#define TAGCACHE_MAGIC 0x5443480b
/* How much to allocate extra space for ramcache. */
#define TAGCACHE_RESERVE 32768
diff --git a/apps/tagtree.c b/apps/tagtree.c
index 59fb8c131f..f9ae0bebd3 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -200,6 +200,7 @@ static int get_tag(int *tag)
MATCH(tag, buf, "comment", tag_comment);
MATCH(tag, buf, "albumartist", tag_albumartist);
MATCH(tag, buf, "ensemble", tag_albumartist);
+ MATCH(tag, buf, "grouping", tag_grouping);
MATCH(tag, buf, "genre", tag_genre);
MATCH(tag, buf, "length", tag_length);
MATCH(tag, buf, "Lm", tag_virt_length_min);
diff --git a/docs/CREDITS b/docs/CREDITS
index 84d8d9f528..4dbc2dfd84 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -311,6 +311,7 @@ Tobias Schladt
John Zhou
Charles Voelger
Gerritt Gonzales
+Dieter Pellkofer
The libmad team
The wavpack team
The ffmpeg team
diff --git a/firmware/export/id3.h b/firmware/export/id3.h
index 8c91456040..2751fde6f5 100644
--- a/firmware/export/id3.h
+++ b/firmware/export/id3.h
@@ -153,6 +153,7 @@ struct mp3entry {
char* composer;
char* comment;
char* albumartist;
+ char* grouping;
int discnum;
int tracknum;
int version;
diff --git a/firmware/id3.c b/firmware/id3.c
index a66318b2fc..851aa83a5a 100644
--- a/firmware/id3.c
+++ b/firmware/id3.c
@@ -460,7 +460,9 @@ static const struct tag_resolver taglist[] = {
{ "TYE", 3, offsetof(struct mp3entry, year_string), &parseyearnum, false },
{ "TCOM", 4, offsetof(struct mp3entry, composer), NULL, false },
{ "TPE2", 4, offsetof(struct mp3entry, albumartist), NULL, false },
- { "TP2", 3, offsetof(struct mp3entry, albumartist), NULL, false },
+ { "TP2", 3, offsetof(struct mp3entry, albumartist), NULL, false },
+ { "TIT1", 4, offsetof(struct mp3entry, grouping), NULL, false },
+ { "TT1", 3, offsetof(struct mp3entry, grouping), NULL, false },
{ "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 },
@@ -1192,6 +1194,8 @@ void adjust_mp3entry(struct mp3entry *entry, void *dest, void *orig)
entry->comment += offset;
if (entry->albumartist)
entry->albumartist += offset;
+ if (entry->grouping)
+ entry->grouping += offset;
#if CONFIG_CODEC == SWCODEC
if (entry->track_gain_string)
entry->track_gain_string += offset;