mirror of
http://mpg123.de/trunk/.git
synced 2025-07-31 13:24:22 +03:00
Some things relating to ID3v2 handling:
- the named entries for artist, album, etc in struct mpg123_id3v2 are now pointers... - ... into the new arrays of ID3v2 text, comment and extra (TXXX) data - That makes a lot more info directly accessible; which id3dump now does access - adapted id3print.c to the pointer thing (by dropping some & ;-) - mpg123_copy_string() now produces an empty string (_not_ "") when copying from NULL id3.c got a bit of structure, but the main parsing function is still to bloated. In general, I hope this change of ID3v2 handling makes sense. You have more data plus the possibility to easily loop through the entries... git-svn-id: svn://scm.orgis.org/mpg123/trunk@1205 35dc7657-300d-0410-a2e5-dc2837fedb53
This commit is contained in:
@ -87,18 +87,56 @@ void print_v2(mpg123_id3v2 *v2)
|
|||||||
int i;
|
int i;
|
||||||
const char *names[] = { "Title", "Artist", "Album", "Year", "Comment", "Genre" };
|
const char *names[] = { "Title", "Artist", "Album", "Year", "Comment", "Genre" };
|
||||||
mpg123_string *sources[sizeof(names)/sizeof(char*)];
|
mpg123_string *sources[sizeof(names)/sizeof(char*)];
|
||||||
sources[0] = &v2->title;
|
sources[0] = v2->title;
|
||||||
sources[1] = &v2->artist;
|
sources[1] = v2->artist;
|
||||||
sources[2] = &v2->album;
|
sources[2] = v2->album;
|
||||||
sources[3] = &v2->year;
|
sources[3] = v2->year;
|
||||||
sources[4] = v2->generic_comment;
|
sources[4] = v2->comment;
|
||||||
sources[5] = &v2->genre;
|
sources[5] = v2->genre;
|
||||||
|
printf("title = %p\n", (void*)v2->title);
|
||||||
for(i=0; i<V1FIELDS; ++i)
|
for(i=0; i<V1FIELDS; ++i)
|
||||||
{
|
{
|
||||||
print_lines(names[i], sources[i]);
|
print_lines(names[i], sources[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void print_raw_v2(mpg123_id3v2 *v2)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for(i=0; i<v2->texts; ++i)
|
||||||
|
{
|
||||||
|
char id[5];
|
||||||
|
memcpy(id, v2->text[i].id, 4);
|
||||||
|
id[4] = 0;
|
||||||
|
printf("%p %s\n", (void*)(&v2->text[i].text), id);
|
||||||
|
print_lines("", &v2->text[i].text);
|
||||||
|
}
|
||||||
|
for(i=0; i<v2->extras; ++i)
|
||||||
|
{
|
||||||
|
char id[5];
|
||||||
|
memcpy(id, v2->extra[i].id, 4);
|
||||||
|
id[4] = 0;
|
||||||
|
printf( "%s description(%s)\n",
|
||||||
|
id,
|
||||||
|
v2->extra[i].description.fill ? v2->extra[i].description.p : "" );
|
||||||
|
print_lines("", &v2->extra[i].text);
|
||||||
|
}
|
||||||
|
for(i=0; i<v2->comments; ++i)
|
||||||
|
{
|
||||||
|
char id[5];
|
||||||
|
char lang[3];
|
||||||
|
memcpy(id, v2->comment_list[i].id, 4);
|
||||||
|
id[4] = 0;
|
||||||
|
memcpy(lang, v2->comment_list[i].lang, 3);
|
||||||
|
lang[3] = 0;
|
||||||
|
printf( "%s description(%s) language(%s): \n",
|
||||||
|
id,
|
||||||
|
v2->comment_list[i].description.fill ? v2->comment_list[i].description.p : "",
|
||||||
|
lang );
|
||||||
|
print_lines("", &v2->comment_list[i].text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -112,7 +150,7 @@ int main(int argc, char **argv)
|
|||||||
mpg123_init();
|
mpg123_init();
|
||||||
m = mpg123_new(NULL, NULL);
|
m = mpg123_new(NULL, NULL);
|
||||||
mpg123_param(m, MPG123_VERBOSE, 4, 0);
|
mpg123_param(m, MPG123_VERBOSE, 4, 0);
|
||||||
|
|
||||||
for(i=1; i < argc; ++i)
|
for(i=1; i < argc; ++i)
|
||||||
{
|
{
|
||||||
mpg123_id3v1 *v1;
|
mpg123_id3v1 *v1;
|
||||||
@ -128,11 +166,14 @@ mpg123_param(m, MPG123_VERBOSE, 4, 0);
|
|||||||
if(meta & MPG123_ID3 && mpg123_id3(m, &v1, &v2) == MPG123_OK)
|
if(meta & MPG123_ID3 && mpg123_id3(m, &v1, &v2) == MPG123_OK)
|
||||||
{
|
{
|
||||||
printf("Tag data on %s:\n", argv[i]);
|
printf("Tag data on %s:\n", argv[i]);
|
||||||
printf("\n==== ID3v1 ====\n");
|
printf("\n==== ID3v1 ====\n");
|
||||||
if(v1 != NULL) print_v1(v1);
|
if(v1 != NULL) print_v1(v1);
|
||||||
|
|
||||||
printf("\n==== ID3v2 ====\n");
|
printf("\n==== ID3v2 ====\n");
|
||||||
if(v2 != NULL) print_v2(v2);
|
if(v2 != NULL) print_v2(v2);
|
||||||
|
|
||||||
|
printf("\n==== ID3v2 Raw frames ====\n");
|
||||||
|
if(v2 != NULL) print_raw_v2(v2);
|
||||||
}
|
}
|
||||||
else printf("Nothing found for %s.\n", argv[i]);
|
else printf("Nothing found for %s.\n", argv[i]);
|
||||||
|
|
||||||
|
@ -33,12 +33,12 @@ void print_id3_tag(mpg123_handle *mh, int long_id3, FILE *out)
|
|||||||
if(v1 == NULL && v2 == NULL) return;
|
if(v1 == NULL && v2 == NULL) return;
|
||||||
if(v2 != NULL) /* fill from ID3v2 data */
|
if(v2 != NULL) /* fill from ID3v2 data */
|
||||||
{
|
{
|
||||||
transform(&tag[TITLE], &v2->title);
|
transform(&tag[TITLE], v2->title);
|
||||||
transform(&tag[ARTIST], &v2->artist);
|
transform(&tag[ARTIST], v2->artist);
|
||||||
transform(&tag[ALBUM], &v2->album);
|
transform(&tag[ALBUM], v2->album);
|
||||||
transform(&tag[COMMENT], v2->generic_comment);
|
transform(&tag[COMMENT], v2->comment);
|
||||||
transform(&tag[YEAR], &v2->year);
|
transform(&tag[YEAR], v2->year);
|
||||||
transform(&tag[GENRE], &v2->genre);
|
transform(&tag[GENRE], v2->genre);
|
||||||
}
|
}
|
||||||
if(v1 != NULL) /* fill gaps with ID3v1 data */
|
if(v1 != NULL) /* fill gaps with ID3v1 data */
|
||||||
{
|
{
|
||||||
|
@ -34,80 +34,131 @@ const int encoding_widths[4] = { 1, 2, 2, 1 };
|
|||||||
void init_id3(mpg123_handle *fr)
|
void init_id3(mpg123_handle *fr)
|
||||||
{
|
{
|
||||||
fr->id3v2.version = 0; /* nothing there */
|
fr->id3v2.version = 0; /* nothing there */
|
||||||
mpg123_init_string(&fr->id3v2.title);
|
fr->id3v2.title = NULL;
|
||||||
mpg123_init_string(&fr->id3v2.artist);
|
fr->id3v2.artist = NULL;
|
||||||
mpg123_init_string(&fr->id3v2.album);
|
fr->id3v2.album = NULL;
|
||||||
mpg123_init_string(&fr->id3v2.year);
|
fr->id3v2.year = NULL;
|
||||||
mpg123_init_string(&fr->id3v2.genre);
|
fr->id3v2.genre = NULL;
|
||||||
fr->id3v2.comments = 0;
|
fr->id3v2.comments = 0;
|
||||||
fr->id3v2.comment = NULL;
|
fr->id3v2.comment_list = NULL;
|
||||||
fr->id3v2.generic_comment = NULL;
|
fr->id3v2.texts = 0;
|
||||||
|
fr->id3v2.text = NULL;
|
||||||
|
fr->id3v2.extras = 0;
|
||||||
|
fr->id3v2.extra = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_comment(mpg123_handle *mh)
|
/* Managing of the text, comment and extra lists. */
|
||||||
|
|
||||||
|
/* Initialize one element. */
|
||||||
|
static void init_mpg123_text(mpg123_text *txt)
|
||||||
|
{
|
||||||
|
mpg123_init_string(&txt->text);
|
||||||
|
mpg123_init_string(&txt->description);
|
||||||
|
txt->id[0] = 0;
|
||||||
|
txt->id[1] = 0;
|
||||||
|
txt->id[2] = 0;
|
||||||
|
txt->id[3] = 0;
|
||||||
|
txt->lang[0] = 0;
|
||||||
|
txt->lang[1] = 0;
|
||||||
|
txt->lang[2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free memory of one element. */
|
||||||
|
static void free_mpg123_text(mpg123_text *txt)
|
||||||
|
{
|
||||||
|
mpg123_free_string(&txt->text);
|
||||||
|
mpg123_init_string(&txt->description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free memory of whole list. */
|
||||||
|
#define free_comment(mh) free_id3_text(&((mh)->id3v2.comment_list), &((mh)->id3v2.comments))
|
||||||
|
#define free_text(mh) free_id3_text(&((mh)->id3v2.text), &((mh)->id3v2.texts))
|
||||||
|
#define free_extra(mh) free_id3_text(&((mh)->id3v2.extra), &((mh)->id3v2.extras))
|
||||||
|
static void free_id3_text(mpg123_text **list, size_t *size)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
mpg123_id3v2 *id = &mh->id3v2;
|
for(i=0; i<*size; ++i) free_mpg123_text(&((*list)[i]));
|
||||||
for(i=0; i<id->comments; ++i)
|
|
||||||
{
|
free(*list);
|
||||||
mpg123_free_string(&id->comment[i].description);
|
*list = NULL;
|
||||||
mpg123_free_string(&id->comment[i].text);
|
*size = 0;
|
||||||
}
|
|
||||||
free(id->comment);
|
|
||||||
id->comment = NULL;
|
|
||||||
id->comments = 0;
|
|
||||||
id->generic_comment = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static mpg123_comment *add_comment(mpg123_handle *mh)
|
/* Add items to the list. */
|
||||||
|
#define add_comment(mh) add_id3_text(&((mh)->id3v2.comment_list), &((mh)->id3v2.comments))
|
||||||
|
#define add_text(mh) add_id3_text(&((mh)->id3v2.text), &((mh)->id3v2.texts))
|
||||||
|
#define add_extra(mh) add_id3_text(&((mh)->id3v2.extra), &((mh)->id3v2.extras))
|
||||||
|
static mpg123_text *add_id3_text(mpg123_text **list, size_t *size)
|
||||||
{
|
{
|
||||||
mpg123_id3v2 *id = &mh->id3v2;
|
mpg123_text *x = safe_realloc(*list, sizeof(mpg123_text)*(*size+1));
|
||||||
mpg123_comment *x = safe_realloc(id->comment, sizeof(mpg123_comment)*(id->comments+1));
|
|
||||||
if(x == NULL) return NULL; /* bad */
|
if(x == NULL) return NULL; /* bad */
|
||||||
|
|
||||||
id->comment = x;
|
*list = x;
|
||||||
id->comments += 1;
|
*size += 1;
|
||||||
id->comment[id->comments-1].lang[0] = 0; /* empty... */
|
init_mpg123_text(&((*list)[*size-1]));
|
||||||
mpg123_init_string(&id->comment[id->comments-1].description);
|
|
||||||
mpg123_init_string(&id->comment[id->comments-1].text);
|
return &((*list)[*size-1]); /* Return pointer to the added text. */
|
||||||
return &id->comment[id->comments-1]; /* Return pointer to the added comment. */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pop_comment(mpg123_handle *mh)
|
/* Remove the last item. */
|
||||||
|
#define pop_comment(mh) pop_id3_text(&((mh)->id3v2.comment_list), &((mh)->id3v2.comments))
|
||||||
|
#define pop_text(mh) pop_id3_text(&((mh)->id3v2.text), &((mh)->id3v2.texts))
|
||||||
|
#define pop_extra(mh) pop_id3_text(&((mh)->id3v2.extra), &((mh)->id3v2.extras))
|
||||||
|
static void pop_id3_text(mpg123_text **list, size_t *size)
|
||||||
{
|
{
|
||||||
mpg123_comment *x;
|
mpg123_text *x;
|
||||||
mpg123_id3v2 *id = &mh->id3v2;
|
if(*size < 1) return;
|
||||||
if(id->comments < 1) return;
|
|
||||||
|
|
||||||
mpg123_free_string(&id->comment[id->comments-1].description);
|
free_mpg123_text(&((*list)[*size-1]));
|
||||||
mpg123_free_string(&id->comment[id->comments-1].text);
|
if(*size > 1)
|
||||||
x = safe_realloc(id->comment, sizeof(mpg123_comment)*(id->comments-1));
|
|
||||||
if(x != NULL)
|
|
||||||
{
|
{
|
||||||
id->comment = x;
|
x = safe_realloc(*list, sizeof(mpg123_text)*(*size-1));
|
||||||
id->comments -= 1;
|
if(x != NULL){ *list = x; *size -= 1; }
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
free(*list);
|
||||||
|
*list = NULL;
|
||||||
|
*size = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* OK, back t the higher level functions. */
|
||||||
|
|
||||||
void exit_id3(mpg123_handle *fr)
|
void exit_id3(mpg123_handle *fr)
|
||||||
{
|
{
|
||||||
mpg123_free_string(&fr->id3v2.title);
|
|
||||||
mpg123_free_string(&fr->id3v2.artist);
|
|
||||||
mpg123_free_string(&fr->id3v2.album);
|
|
||||||
mpg123_free_string(&fr->id3v2.year);
|
|
||||||
mpg123_free_string(&fr->id3v2.genre);
|
|
||||||
free_comment(fr);
|
free_comment(fr);
|
||||||
|
free_extra(fr);
|
||||||
|
free_text(fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_id3(mpg123_handle *fr)
|
void reset_id3(mpg123_handle *fr)
|
||||||
{
|
{
|
||||||
fr->id3v2.version = 0;
|
exit_id3(fr);
|
||||||
fr->id3v2.title.fill = 0;
|
init_id3(fr);
|
||||||
fr->id3v2.artist.fill = 0;
|
}
|
||||||
fr->id3v2.album.fill = 0;
|
|
||||||
fr->id3v2.year.fill = 0;
|
/* Set the id3v2.artist id3v2.title ... links to elements of the array. */
|
||||||
fr->id3v2.genre.fill = 0;
|
void id3_link(mpg123_handle *fr)
|
||||||
free_comment(fr);
|
{
|
||||||
|
size_t i;
|
||||||
|
mpg123_id3v2 *v2 = &fr->id3v2;
|
||||||
|
for(i=0; i<v2->texts; ++i)
|
||||||
|
{
|
||||||
|
mpg123_text *entry = &v2->text[i];
|
||||||
|
fprintf(stderr, "entry %i id %c%c%c%c at %p\n", i, entry->id[0], entry->id[1], entry->id[2], entry->id[3], &entry->text);
|
||||||
|
if (!strncmp("TIT2", entry->id, 4)) v2->title = &entry->text;
|
||||||
|
else if(!strncmp("TALB", entry->id, 4)) v2->album = &entry->text;
|
||||||
|
else if(!strncmp("TPE1", entry->id, 4)) v2->artist = &entry->text;
|
||||||
|
else if(!strncmp("TYER", entry->id, 4)) v2->year = &entry->text;
|
||||||
|
else if(!strncmp("TCON", entry->id, 4)) v2->genre = &entry->text;
|
||||||
|
}
|
||||||
|
for(i=0; i<v2->comments; ++i)
|
||||||
|
{
|
||||||
|
mpg123_text *entry = &v2->comment_list[i];
|
||||||
|
if(entry->description.fill == 0 || entry->description.p[0] == 0)
|
||||||
|
v2->comment = &entry->text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -183,8 +234,23 @@ char *next_text(char* prev, int encoding, size_t limit)
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void process_text(mpg123_handle *fr, char *realdata, size_t realsize, char *id)
|
||||||
|
{
|
||||||
|
/* Text encoding $xx */
|
||||||
|
/* The text (encoded) ... */
|
||||||
|
mpg123_text *t = add_text(fr);
|
||||||
|
if(t == NULL)
|
||||||
|
{
|
||||||
|
if(NOQUIET) error("Unable to attach new text!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(t->id, id, 4);
|
||||||
|
store_id3_text(&t->text, realdata, realsize);
|
||||||
|
if(VERBOSE4) fprintf(stderr, "Note: ID3v2 %c%c%c%c text frame: %s\n", id[0], id[1], id[2], id[3], t->text.p);
|
||||||
|
}
|
||||||
|
|
||||||
/* Store a new comment that perhaps is a RVA / RVA_ALBUM/AUDIOPHILE / RVA_MIX/RADIO one */
|
/* Store a new comment that perhaps is a RVA / RVA_ALBUM/AUDIOPHILE / RVA_MIX/RADIO one */
|
||||||
static void process_comment(mpg123_handle *fr, char *realdata, size_t realsize, int tt)
|
static void process_comment(mpg123_handle *fr, char *realdata, size_t realsize, int rva_level, char *id)
|
||||||
{
|
{
|
||||||
/* Text encoding $xx */
|
/* Text encoding $xx */
|
||||||
/* Language $xx xx xx */
|
/* Language $xx xx xx */
|
||||||
@ -194,13 +260,14 @@ static void process_comment(mpg123_handle *fr, char *realdata, size_t realsize,
|
|||||||
char *lang = realdata+1; /* I'll only use the 3 bytes! */
|
char *lang = realdata+1; /* I'll only use the 3 bytes! */
|
||||||
char *descr = realdata+4;
|
char *descr = realdata+4;
|
||||||
char *text;
|
char *text;
|
||||||
mpg123_comment *xcom = add_comment(fr);
|
mpg123_text *xcom = add_comment(fr);
|
||||||
if(xcom == NULL)
|
if(xcom == NULL)
|
||||||
{
|
{
|
||||||
if(NOQUIET) error("Unable to attach new comment!");
|
if(NOQUIET) error("Unable to attach new comment!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memcpy(xcom->lang, lang, 3);
|
memcpy(xcom->lang, lang, 3);
|
||||||
|
memcpy(xcom->id, id, 4);
|
||||||
xcom->lang[3] = 0;
|
xcom->lang[3] = 0;
|
||||||
/* Now I can abuse a byte from lang for the encoding. */
|
/* Now I can abuse a byte from lang for the encoding. */
|
||||||
descr[-1] = encoding;
|
descr[-1] = encoding;
|
||||||
@ -233,76 +300,79 @@ static void process_comment(mpg123_handle *fr, char *realdata, size_t realsize,
|
|||||||
|| !strcasecmp(xcom->description.p, "rva_audiophile")
|
|| !strcasecmp(xcom->description.p, "rva_audiophile")
|
||||||
|| !strcasecmp(xcom->description.p, "rva_user"))
|
|| !strcasecmp(xcom->description.p, "rva_user"))
|
||||||
rva_mode = 1;
|
rva_mode = 1;
|
||||||
if((rva_mode > -1) && (fr->rva.level[rva_mode] <= tt+1))
|
if((rva_mode > -1) && (fr->rva.level[rva_mode] <= rva_level))
|
||||||
{
|
{
|
||||||
fr->rva.gain[rva_mode] = atof(xcom->text.p);
|
fr->rva.gain[rva_mode] = atof(xcom->text.p);
|
||||||
if(VERBOSE3) fprintf(stderr, "Note: RVA value %fdB\n", fr->rva.gain[rva_mode]);
|
if(VERBOSE3) fprintf(stderr, "Note: RVA value %fdB\n", fr->rva.gain[rva_mode]);
|
||||||
fr->rva.peak[rva_mode] = 0;
|
fr->rva.peak[rva_mode] = 0;
|
||||||
fr->rva.level[rva_mode] = tt+1;
|
fr->rva.level[rva_mode] = rva_level;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Mark the last found generic comment. */
|
|
||||||
if(xcom->description.fill == 0 || xcom->description.p[0] == 0)
|
|
||||||
fr->id3v2.generic_comment = &xcom->text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_extra(mpg123_handle *fr, char* realdata, size_t realsize, int tt)
|
void process_extra(mpg123_handle *fr, char* realdata, size_t realsize, int rva_level, char *id)
|
||||||
{
|
{
|
||||||
/* Text encoding $xx */
|
/* Text encoding $xx */
|
||||||
/* Description ... $00 (00) */
|
/* Description ... $00 (00) */
|
||||||
/* Text ... */
|
/* Text ... */
|
||||||
mpg123_string work;
|
|
||||||
char encoding = realdata[0];
|
char encoding = realdata[0];
|
||||||
char *descr = realdata+1; /* remember, the encoding is descr[-1] */
|
char *descr = realdata+1; /* remember, the encoding is descr[-1] */
|
||||||
char *text = next_text(descr, encoding, realsize-(descr-realdata));
|
char *text;
|
||||||
|
mpg123_text *xex;
|
||||||
|
text = next_text(descr, encoding, realsize-(descr-realdata));
|
||||||
if(text == NULL)
|
if(text == NULL)
|
||||||
{
|
{
|
||||||
if(NOQUIET) error("No extra frame text / valid description?");
|
if(NOQUIET) error("No extra frame text / valid description?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mpg123_init_string(&work);
|
xex = add_extra(fr);
|
||||||
store_id3_text(&work, descr-1, text-descr+1);
|
if(xex == NULL)
|
||||||
if(work.fill > 0)
|
{
|
||||||
|
if(NOQUIET) error("Unable to attach new extra text!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(xex->id, id, 4);
|
||||||
|
store_id3_text(&xex->description, descr-1, text-descr+1);
|
||||||
|
text[-1] = encoding;
|
||||||
|
store_id3_text(&xex->text, text-1, realsize-(text-realdata)+1);
|
||||||
|
if(xex->description.fill > 0)
|
||||||
{
|
{
|
||||||
int is_peak = 0;
|
int is_peak = 0;
|
||||||
int rva_mode = -1; /* mix / album */
|
int rva_mode = -1; /* mix / album */
|
||||||
|
|
||||||
if(!strncasecmp(work.p, "replaygain_track_",17))
|
if(!strncasecmp(xex->description.p, "replaygain_track_",17))
|
||||||
{
|
{
|
||||||
debug("ID3v2: track gain/peak");
|
debug("ID3v2: track gain/peak");
|
||||||
rva_mode = 0;
|
rva_mode = 0;
|
||||||
if(!strcasecmp(work.p, "replaygain_track_peak")) is_peak = 1;
|
if(!strcasecmp(xex->description.p, "replaygain_track_peak")) is_peak = 1;
|
||||||
else if(strcasecmp(work.p, "replaygain_track_gain")) rva_mode = -1;
|
else if(strcasecmp(xex->description.p, "replaygain_track_gain")) rva_mode = -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if(!strncasecmp(work.p, "replaygain_album_",17))
|
if(!strncasecmp(xex->description.p, "replaygain_album_",17))
|
||||||
{
|
{
|
||||||
debug("ID3v2: album gain/peak");
|
debug("ID3v2: album gain/peak");
|
||||||
rva_mode = 1;
|
rva_mode = 1;
|
||||||
if(!strcasecmp(work.p, "replaygain_album_peak")) is_peak = 1;
|
if(!strcasecmp(xex->description.p, "replaygain_album_peak")) is_peak = 1;
|
||||||
else if(strcasecmp(work.p, "replaygain_album_gain")) rva_mode = -1;
|
else if(strcasecmp(xex->description.p, "replaygain_album_gain")) rva_mode = -1;
|
||||||
}
|
}
|
||||||
if((rva_mode > -1) && (fr->rva.level[rva_mode] <= tt+1))
|
if((rva_mode > -1) && (fr->rva.level[rva_mode] <= rva_level))
|
||||||
{
|
{
|
||||||
text[-1] = encoding;
|
if(xex->text.fill > 0)
|
||||||
store_id3_text(&work, text-1, realsize-(text-realdata)+1);
|
|
||||||
if(work.fill > 0)
|
|
||||||
{
|
{
|
||||||
if(is_peak)
|
if(is_peak)
|
||||||
{
|
{
|
||||||
fr->rva.peak[rva_mode] = atof(work.p);
|
fr->rva.peak[rva_mode] = atof(xex->text.p);
|
||||||
if(VERBOSE3) fprintf(stderr, "Note: RVA peak %fdB\n", fr->rva.peak[rva_mode]);
|
if(VERBOSE3) fprintf(stderr, "Note: RVA peak %fdB\n", fr->rva.peak[rva_mode]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fr->rva.gain[rva_mode] = atof(work.p);
|
fr->rva.gain[rva_mode] = atof(xex->text.p);
|
||||||
if(VERBOSE3) fprintf(stderr, "Note: RVA gain %fdB\n", fr->rva.gain[rva_mode]);
|
if(VERBOSE3) fprintf(stderr, "Note: RVA gain %fdB\n", fr->rva.gain[rva_mode]);
|
||||||
}
|
}
|
||||||
fr->rva.level[rva_mode] = tt+1;
|
fr->rva.level[rva_mode] = rva_level;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mpg123_free_string(&work);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -401,9 +471,9 @@ int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes)
|
|||||||
unsigned long pos = tagpos;
|
unsigned long pos = tagpos;
|
||||||
/* level 1,2,3 - 0 is info from lame/info tag! */
|
/* level 1,2,3 - 0 is info from lame/info tag! */
|
||||||
/* rva tags with ascending significance, then general frames */
|
/* rva tags with ascending significance, then general frames */
|
||||||
#define KNOWN_FRAMES 8
|
#define KNOWN_FRAMES 3
|
||||||
const char frame_type[KNOWN_FRAMES][5] = { "COMM", "TXXX", "RVA2", "TPE1", "TALB", "TIT2", "TYER", "TCON" };
|
const char frame_type[KNOWN_FRAMES][5] = { "COMM", "TXXX", "RVA2" }; /* plus all text frames... */
|
||||||
enum { egal = -1, comment, extra, rva2, artist, album, title, year, genre } tt = egal;
|
enum { unknown = -2, text = -1, comment, extra, rva2 } tt = unknown;
|
||||||
/* we may have entered the padding zone or any other strangeness: check if we have valid frame id characters */
|
/* we may have entered the padding zone or any other strangeness: check if we have valid frame id characters */
|
||||||
for(; i< 4; ++i) if( !( ((tagdata[tagpos+i] > 47) && (tagdata[tagpos+i] < 58))
|
for(; i< 4; ++i) if( !( ((tagdata[tagpos+i] > 47) && (tagdata[tagpos+i] < 58))
|
||||||
|| ((tagdata[tagpos+i] > 64) && (tagdata[tagpos+i] < 91)) ) )
|
|| ((tagdata[tagpos+i] > 64) && (tagdata[tagpos+i] < 91)) ) )
|
||||||
@ -450,8 +520,10 @@ int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes)
|
|||||||
|
|
||||||
for(i = 0; i < KNOWN_FRAMES; ++i)
|
for(i = 0; i < KNOWN_FRAMES; ++i)
|
||||||
if(!strncmp(frame_type[i], id, 4)){ tt = i; break; }
|
if(!strncmp(frame_type[i], id, 4)){ tt = i; break; }
|
||||||
|
|
||||||
if(tt != egal)
|
if(id[0] == 'T' && tt != extra) tt = text;
|
||||||
|
|
||||||
|
if(tt != unknown)
|
||||||
{
|
{
|
||||||
int rva_mode = -1; /* mix / album */
|
int rva_mode = -1; /* mix / album */
|
||||||
unsigned long realsize = framesize;
|
unsigned long realsize = framesize;
|
||||||
@ -487,10 +559,10 @@ int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes)
|
|||||||
switch(tt)
|
switch(tt)
|
||||||
{
|
{
|
||||||
case comment:
|
case comment:
|
||||||
process_comment(fr, (char*)realdata, realsize, tt);
|
process_comment(fr, (char*)realdata, realsize, comment+1, id);
|
||||||
break;
|
break;
|
||||||
case extra: /* perhaps foobar2000's work */
|
case extra: /* perhaps foobar2000's work */
|
||||||
process_extra(fr, (char*)realdata, realsize, tt);
|
process_extra(fr, (char*)realdata, realsize, extra+1, id);
|
||||||
break;
|
break;
|
||||||
case rva2: /* "the" RVA tag */
|
case rva2: /* "the" RVA tag */
|
||||||
{
|
{
|
||||||
@ -503,7 +575,7 @@ int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes)
|
|||||||
|| !strncasecmp((char*)realdata, "audiophile", 10)
|
|| !strncasecmp((char*)realdata, "audiophile", 10)
|
||||||
|| !strncasecmp((char*)realdata, "user", 4))
|
|| !strncasecmp((char*)realdata, "user", 4))
|
||||||
rva_mode = 1;
|
rva_mode = 1;
|
||||||
if(fr->rva.level[rva_mode] <= tt+1)
|
if(fr->rva.level[rva_mode] <= rva2+1)
|
||||||
{
|
{
|
||||||
pos += strlen((char*) realdata) + 1;
|
pos += strlen((char*) realdata) + 1;
|
||||||
if(realdata[pos] == 1)
|
if(realdata[pos] == 1)
|
||||||
@ -519,7 +591,7 @@ int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes)
|
|||||||
if(VERBOSE3) fprintf(stderr, "Note: RVA value %fdB\n", fr->rva.gain[rva_mode]);
|
if(VERBOSE3) fprintf(stderr, "Note: RVA value %fdB\n", fr->rva.gain[rva_mode]);
|
||||||
/* heh, the peak value is represented by a number of bits - but in what manner? Skipping that part */
|
/* heh, the peak value is represented by a number of bits - but in what manner? Skipping that part */
|
||||||
fr->rva.peak[rva_mode] = 0;
|
fr->rva.peak[rva_mode] = 0;
|
||||||
fr->rva.level[rva_mode] = tt+1;
|
fr->rva.level[rva_mode] = rva2+1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -528,25 +600,8 @@ int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* non-rva metainfo, simply store... */
|
/* non-rva metainfo, simply store... */
|
||||||
case artist:
|
case text:
|
||||||
debug("ID3v2: parsing artist info");
|
process_text(fr, (char*)realdata, realsize, id);
|
||||||
store_id3_text(&fr->id3v2.artist, (char*) realdata, realsize);
|
|
||||||
break;
|
|
||||||
case album:
|
|
||||||
debug("ID3v2: parsing album info");
|
|
||||||
store_id3_text(&fr->id3v2.album, (char*) realdata, realsize);
|
|
||||||
break;
|
|
||||||
case title:
|
|
||||||
debug("ID3v2: parsing title info");
|
|
||||||
store_id3_text(&fr->id3v2.title, (char*) realdata, realsize);
|
|
||||||
break;
|
|
||||||
case year:
|
|
||||||
debug("ID3v2: parsing year info");
|
|
||||||
store_id3_text(&fr->id3v2.year, (char*) realdata, realsize);
|
|
||||||
break;
|
|
||||||
case genre:
|
|
||||||
debug("ID3v2: parsing genre info");
|
|
||||||
store_id3_text(&fr->id3v2.genre, (char*) realdata, realsize);
|
|
||||||
break;
|
break;
|
||||||
default: error1("ID3v2: unknown frame type %i", tt);
|
default: error1("ID3v2: unknown frame type %i", tt);
|
||||||
}
|
}
|
||||||
|
@ -16,5 +16,6 @@ void init_id3(mpg123_handle *fr);
|
|||||||
void exit_id3(mpg123_handle *fr);
|
void exit_id3(mpg123_handle *fr);
|
||||||
void reset_id3(mpg123_handle *fr);
|
void reset_id3(mpg123_handle *fr);
|
||||||
int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes);
|
int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes);
|
||||||
|
void id3_link(mpg123_handle *fr);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -897,6 +897,7 @@ int mpg123_id3(mpg123_handle *mh, mpg123_id3v1 **v1, mpg123_id3v2 **v2)
|
|||||||
|
|
||||||
if(mh->metaflags & MPG123_ID3)
|
if(mh->metaflags & MPG123_ID3)
|
||||||
{
|
{
|
||||||
|
id3_link(mh);
|
||||||
if(v1 != NULL && mh->rdat.flags & READER_ID3TAG) *v1 = (mpg123_id3v1*) mh->id3buf;
|
if(v1 != NULL && mh->rdat.flags & READER_ID3TAG) *v1 = (mpg123_id3v1*) mh->id3buf;
|
||||||
if(v2 != NULL) *v2 = &mh->id3v2;
|
if(v2 != NULL) *v2 = &mh->id3v2;
|
||||||
mh->metaflags |= MPG123_ID3;
|
mh->metaflags |= MPG123_ID3;
|
||||||
|
@ -566,13 +566,17 @@ EXPORT int mpg123_add_string(mpg123_string* sb, char* stuff);
|
|||||||
* \return 0 on error, 1 on success */
|
* \return 0 on error, 1 on success */
|
||||||
EXPORT int mpg123_set_string(mpg123_string* sb, char* stuff);
|
EXPORT int mpg123_set_string(mpg123_string* sb, char* stuff);
|
||||||
|
|
||||||
/** Sub data structure for ID3v2, for storing comments. */
|
/** Sub data structure for ID3v2, for storing various text fields (including comments).
|
||||||
|
* This is for ID3v2 COMM, TXXX and all the other text fields.
|
||||||
|
* Only COMM and TXXX have a description, only COMM has a language.
|
||||||
|
* You should consult the ID3v2 specification for the use of the various text fields ("frames" in ID3v2 documentation, I use "fields" here to separate from MPEG frames). */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char lang[4]; /**< Three-letter language code (null-terminated). */
|
char lang[3]; /**< Three-letter language code (not terminated). */
|
||||||
|
char id[4]; /**< The ID3v2 text field id, like TALB, TPE2, ... (4 characters, no string termination). */
|
||||||
mpg123_string description; /**< Empty for the generic comment... */
|
mpg123_string description; /**< Empty for the generic comment... */
|
||||||
mpg123_string text; /**< ... */
|
mpg123_string text; /**< ... */
|
||||||
} mpg123_comment;
|
} mpg123_text;
|
||||||
|
|
||||||
/** Data structure for storing IDV3v2 tags.
|
/** Data structure for storing IDV3v2 tags.
|
||||||
* This structure is not a direct binary mapping with the file contents.
|
* This structure is not a direct binary mapping with the file contents.
|
||||||
@ -582,14 +586,20 @@ typedef struct
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
unsigned char version; /**< 3 or 4 for ID3v2.3 or ID3v2.4. */
|
unsigned char version; /**< 3 or 4 for ID3v2.3 or ID3v2.4. */
|
||||||
mpg123_string title; /**< Title string. */
|
mpg123_string *title; /**< Title string (pointer into text_list). */
|
||||||
mpg123_string artist; /**< Artist string. */
|
mpg123_string *artist; /**< Artist string (pointer into text_list). */
|
||||||
mpg123_string album; /**< Album string. */
|
mpg123_string *album; /**< Album string (pointer into text_list). */
|
||||||
mpg123_string year; /**< The year as a string. */
|
mpg123_string *year; /**< The year as a string (pointer into text_list). */
|
||||||
mpg123_string genre; /**< Genre String. The genre string(s) may very well need postprocessing, esp. for ID3v2.3. */
|
mpg123_string *genre; /**< Genre String (pointer into text_list). The genre string(s) may very well need postprocessing, esp. for ID3v2.3. */
|
||||||
size_t comments; /**< Number of comments. */
|
mpg123_string *comment; /**< Pointer to last encountered comment text with empty description. */
|
||||||
mpg123_comment *comment; /**< Array of comments. */
|
/* Encountered ID3v2 fields are appended to these lists.
|
||||||
mpg123_string *generic_comment; /**< Pointer to last encountered comment text with empty description. */
|
There can be multiple occurences, the pointers above always point to the last encountered data. */
|
||||||
|
mpg123_text *comment_list; /**< Array of comments. */
|
||||||
|
size_t comments; /**< Number of comments. */
|
||||||
|
mpg123_text *text; /**< Array of ID3v2 text fields */
|
||||||
|
size_t texts; /**< Numer of text fields. */
|
||||||
|
mpg123_text *extra; /**< The array of extra (TXXX) fields. */
|
||||||
|
size_t extras; /**< Number of extra text (TXXX) fields. */
|
||||||
} mpg123_id3v2;
|
} mpg123_id3v2;
|
||||||
|
|
||||||
/** Data structure for ID3v1 tags (the last 128 bytes of a file).
|
/** Data structure for ID3v1 tags (the last 128 bytes of a file).
|
||||||
|
@ -53,14 +53,25 @@ int mpg123_resize_string(mpg123_string* sb, size_t new)
|
|||||||
|
|
||||||
int mpg123_copy_string(mpg123_string* from, mpg123_string* to)
|
int mpg123_copy_string(mpg123_string* from, mpg123_string* to)
|
||||||
{
|
{
|
||||||
|
size_t fill;
|
||||||
|
char *text;
|
||||||
if(to == NULL) return -1;
|
if(to == NULL) return -1;
|
||||||
|
|
||||||
if(from == NULL) return mpg123_set_string(to, "");
|
if(from == NULL)
|
||||||
|
|
||||||
if(mpg123_resize_string(to, from->fill))
|
|
||||||
{
|
{
|
||||||
memcpy(to->p, from->p, to->size);
|
fill = 0;
|
||||||
to->fill = to->size;
|
text = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fill = from->fill;
|
||||||
|
text = from->p;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mpg123_resize_string(to, fill))
|
||||||
|
{
|
||||||
|
memcpy(to->p, text, fill);
|
||||||
|
to->fill = fill;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else return 0;
|
else return 0;
|
||||||
|
Reference in New Issue
Block a user