mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-12-10 21:55:10 -05:00
Added support for ID3V2 ReplayGain tags (as written by Foobar). Generalized the replaygain tag parsing a bit, to cut down the code size (APE tags should use this as well, but as it requires larger changes, it will have to wait for another commit). Also fixed a bug in the ID3V2 parser; ISO-8859-1 strings could confuse the main parsing loop (causing bufferpos to come out of sync).
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7243 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
e44372ef18
commit
988ea2cffc
7 changed files with 236 additions and 97 deletions
|
|
@ -118,6 +118,7 @@ enum codec_status codec_start(struct codec_api* api)
|
||||||
frequency_divider = 441;
|
frequency_divider = 441;
|
||||||
|
|
||||||
ci->configure(DSP_SET_FREQUENCY, (int *)ci->id3->frequency);
|
ci->configure(DSP_SET_FREQUENCY, (int *)ci->id3->frequency);
|
||||||
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
ci->request_buffer(&size, ci->id3->first_frame_offset);
|
ci->request_buffer(&size, ci->id3->first_frame_offset);
|
||||||
ci->advance_buffer(size);
|
ci->advance_buffer(size);
|
||||||
|
|
@ -144,7 +145,7 @@ enum codec_status codec_start(struct codec_api* api)
|
||||||
samplecount = ci->id3->length * frequency_divider / 10;
|
samplecount = ci->id3->length * frequency_divider / 10;
|
||||||
samplesdone = ci->id3->elapsed * frequency_divider / 10;
|
samplesdone = ci->id3->elapsed * frequency_divider / 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is the decoding loop. */
|
/* This is the decoding loop. */
|
||||||
while (1) {
|
while (1) {
|
||||||
ci->yield();
|
ci->yield();
|
||||||
|
|
|
||||||
|
|
@ -528,7 +528,7 @@ static bool get_apetag_info (struct mp3entry *entry, int fd)
|
||||||
|
|
||||||
if (rem_space > 1 &&
|
if (rem_space > 1 &&
|
||||||
get_apetag_item (&temp_apetag, "replaygain_track_gain", temp_buffer, rem_space)) {
|
get_apetag_item (&temp_apetag, "replaygain_track_gain", temp_buffer, rem_space)) {
|
||||||
entry->track_gain = get_replaygain (entry->track_gain_str = temp_buffer);
|
entry->track_gain = get_replaygain (entry->track_gain_string = temp_buffer);
|
||||||
str_space = strlen (temp_buffer) + 1;
|
str_space = strlen (temp_buffer) + 1;
|
||||||
temp_buffer += str_space;
|
temp_buffer += str_space;
|
||||||
rem_space -= str_space;
|
rem_space -= str_space;
|
||||||
|
|
@ -536,7 +536,7 @@ static bool get_apetag_info (struct mp3entry *entry, int fd)
|
||||||
|
|
||||||
if (rem_space > 1 &&
|
if (rem_space > 1 &&
|
||||||
get_apetag_item (&temp_apetag, "replaygain_album_gain", temp_buffer, rem_space)) {
|
get_apetag_item (&temp_apetag, "replaygain_album_gain", temp_buffer, rem_space)) {
|
||||||
entry->album_gain = get_replaygain (entry->album_gain_str = temp_buffer);
|
entry->album_gain = get_replaygain (entry->album_gain_string = temp_buffer);
|
||||||
str_space = strlen (temp_buffer) + 1;
|
str_space = strlen (temp_buffer) + 1;
|
||||||
temp_buffer += str_space;
|
temp_buffer += str_space;
|
||||||
rem_space -= str_space;
|
rem_space -= str_space;
|
||||||
|
|
@ -910,37 +910,13 @@ static bool get_vorbis_comments (struct mp3entry *entry, int fd)
|
||||||
} else if (strncasecmp(temp, "TRACKNUMBER=", 12) == 0) {
|
} else if (strncasecmp(temp, "TRACKNUMBER=", 12) == 0) {
|
||||||
name_length = 11;
|
name_length = 11;
|
||||||
p = &(entry->track_string);
|
p = &(entry->track_string);
|
||||||
} else if ((strncasecmp(temp, "RG_RADIO=", 9) == 0)
|
|
||||||
&& !entry->track_gain) {
|
|
||||||
entry->track_gain = get_replaygain(&temp[9]);
|
|
||||||
name_length = 8;
|
|
||||||
p = &(entry->track_gain_str);
|
|
||||||
} else if (strncasecmp(temp, "REPLAYGAIN_TRACK_GAIN=", 22) == 0) {
|
|
||||||
entry->track_gain = get_replaygain(&temp[22]);
|
|
||||||
name_length = 21;
|
|
||||||
p = &(entry->track_gain_str);
|
|
||||||
} else if ((strncasecmp(temp, "RG_AUDIOPHILE=", 14) == 0)
|
|
||||||
&& !entry->album_gain) {
|
|
||||||
entry->album_gain = get_replaygain(&temp[14]);
|
|
||||||
name_length = 13;
|
|
||||||
p = &(entry->album_gain_str);
|
|
||||||
} else if (strncasecmp(temp, "REPLAYGAIN_ALBUM_GAIN=", 22) == 0) {
|
|
||||||
entry->album_gain = get_replaygain(&temp[22]);
|
|
||||||
name_length = 21;
|
|
||||||
p = &(entry->album_gain_str);
|
|
||||||
} else if ((strncasecmp(temp, "RG_PEAK=", 8) == 0)
|
|
||||||
&& !entry->track_peak) {
|
|
||||||
entry->track_peak = get_replaypeak(&temp[8]);
|
|
||||||
p = NULL;
|
|
||||||
} else if (strncasecmp(temp, "REPLAYGAIN_TRACK_PEAK=", 22) == 0) {
|
|
||||||
entry->track_peak = get_replaypeak(&temp[22]);
|
|
||||||
p = NULL;
|
|
||||||
} else if (strncasecmp(temp, "REPLAYGAIN_ALBUM_PEAK=", 22) == 0) {
|
|
||||||
entry->album_peak = get_replaypeak(&temp[22]);
|
|
||||||
p = NULL;
|
|
||||||
} else {
|
} else {
|
||||||
|
int value_length = parse_replaygain(temp, NULL, entry, buffer,
|
||||||
|
buffer_remaining);
|
||||||
|
buffer_remaining -= value_length;
|
||||||
|
buffer += value_length;
|
||||||
p = NULL;
|
p = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p) {
|
if (p) {
|
||||||
comment_length -= (name_length + 1);
|
comment_length -= (name_length + 1);
|
||||||
|
|
|
||||||
|
|
@ -1388,15 +1388,15 @@ bool browse_id3(void)
|
||||||
#if CONFIG_HWCODEC == MASNONE
|
#if CONFIG_HWCODEC == MASNONE
|
||||||
case 11:
|
case 11:
|
||||||
lcd_puts(0, 0, str(LANG_ID3_TRACK_GAIN));
|
lcd_puts(0, 0, str(LANG_ID3_TRACK_GAIN));
|
||||||
lcd_puts(0, 1, id3->track_gain_str
|
lcd_puts(0, 1, id3->track_gain_string
|
||||||
? id3->track_gain_str
|
? id3->track_gain_string
|
||||||
: (char*) str(LANG_ID3_NO_GAIN));
|
: (char*) str(LANG_ID3_NO_GAIN));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 12:
|
case 12:
|
||||||
lcd_puts(0, 0, str(LANG_ID3_ALBUM_GAIN));
|
lcd_puts(0, 0, str(LANG_ID3_ALBUM_GAIN));
|
||||||
lcd_puts(0, 1, id3->album_gain_str
|
lcd_puts(0, 1, id3->album_gain_string
|
||||||
? id3->album_gain_str
|
? id3->album_gain_string
|
||||||
: (char*) str(LANG_ID3_NO_GAIN));
|
: (char*) str(LANG_ID3_NO_GAIN));
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,8 @@ struct mp3entry {
|
||||||
/* replaygain support */
|
/* replaygain support */
|
||||||
|
|
||||||
#if CONFIG_HWCODEC == MASNONE
|
#if CONFIG_HWCODEC == MASNONE
|
||||||
char* track_gain_str;
|
char* track_gain_string;
|
||||||
char* album_gain_str;
|
char* album_gain_string;
|
||||||
long track_gain; /* 7.24 signed fixed point. 0 for no gain. */
|
long track_gain; /* 7.24 signed fixed point. 0 for no gain. */
|
||||||
long album_gain;
|
long album_gain;
|
||||||
long track_peak; /* 7.24 signed fixed point. 0 for no peak. */
|
long track_peak; /* 7.24 signed fixed point. 0 for no peak. */
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,11 @@
|
||||||
#ifndef _REPLAYGAIN_H
|
#ifndef _REPLAYGAIN_H
|
||||||
#define _REPLAYGAIN_H
|
#define _REPLAYGAIN_H
|
||||||
|
|
||||||
|
#include "id3.h"
|
||||||
|
|
||||||
long get_replaygain(const char* str);
|
long get_replaygain(const char* str);
|
||||||
long get_replaypeak(const char* str);
|
long get_replaypeak(const char* str);
|
||||||
|
long parse_replaygain(const char* key, const char* value,
|
||||||
|
struct mp3entry* entry, char* buffer, int length);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
168
firmware/id3.c
168
firmware/id3.c
|
|
@ -41,6 +41,7 @@
|
||||||
#include "id3.h"
|
#include "id3.h"
|
||||||
#include "mp3data.h"
|
#include "mp3data.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
#include "replaygain.h"
|
||||||
|
|
||||||
#define UNSYNC(b0,b1,b2,b3) (((long)(b0 & 0x7F) << (3*7)) | \
|
#define UNSYNC(b0,b1,b2,b3) (((long)(b0 & 0x7F) << (3*7)) | \
|
||||||
((long)(b1 & 0x7F) << (2*7)) | \
|
((long)(b1 & 0x7F) << (2*7)) | \
|
||||||
|
|
@ -163,6 +164,10 @@ char* id3_get_codec(const struct mp3entry* id3)
|
||||||
Many ID3 symbolic names come in more than one form. You can add both
|
Many ID3 symbolic names come in more than one form. You can add both
|
||||||
forms, each referencing the same variable in struct mp3entry.
|
forms, each referencing the same variable in struct mp3entry.
|
||||||
If both forms are present, the last found will be used.
|
If both forms are present, the last found will be used.
|
||||||
|
Note that the offset can be zero, in which case no entry will be set
|
||||||
|
in the mp3entry struct; the frame is still read into the buffer and
|
||||||
|
the special processing function is called (several times, if there
|
||||||
|
are several frames with the same name).
|
||||||
|
|
||||||
4. Alternately, use the TAG_LIST_ENTRY macro with
|
4. Alternately, use the TAG_LIST_ENTRY macro with
|
||||||
ID3 tag symbolic name,
|
ID3 tag symbolic name,
|
||||||
|
|
@ -305,6 +310,34 @@ static int parsegenre( struct mp3entry* entry, char* tag, int bufferpos )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if CONFIG_HWCODEC == MASNONE
|
||||||
|
/* parse user defined text, looking for replaygain information. */
|
||||||
|
static int parseuser( struct mp3entry* entry, char* tag, int bufferpos )
|
||||||
|
{
|
||||||
|
char* value = NULL;
|
||||||
|
int desc_len = strlen(tag);
|
||||||
|
int value_len = 0;
|
||||||
|
|
||||||
|
if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) {
|
||||||
|
/* At least part of the value was read, so we can safely try to
|
||||||
|
* parse it
|
||||||
|
*/
|
||||||
|
|
||||||
|
value = tag + desc_len + 1;
|
||||||
|
value_len = parse_replaygain(tag, value, entry, tag,
|
||||||
|
bufferpos - (tag - entry->id3v2buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value_len) {
|
||||||
|
bufferpos = tag - entry->id3v2buf + value_len;
|
||||||
|
} else {
|
||||||
|
bufferpos = tag - entry->id3v2buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufferpos;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static const struct tag_resolver taglist[] = {
|
static const struct tag_resolver taglist[] = {
|
||||||
{ "TPE1", 4, offsetof(struct mp3entry, artist), NULL },
|
{ "TPE1", 4, offsetof(struct mp3entry, artist), NULL },
|
||||||
{ "TP1", 3, offsetof(struct mp3entry, artist), NULL },
|
{ "TP1", 3, offsetof(struct mp3entry, artist), NULL },
|
||||||
|
|
@ -319,6 +352,9 @@ static const struct tag_resolver taglist[] = {
|
||||||
{ "TCOM", 4, offsetof(struct mp3entry, composer), NULL },
|
{ "TCOM", 4, offsetof(struct mp3entry, composer), NULL },
|
||||||
{ "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre },
|
{ "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre },
|
||||||
{ "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre },
|
{ "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre },
|
||||||
|
#if CONFIG_HWCODEC == MASNONE
|
||||||
|
{ "TXXX", 4, 0, &parseuser },
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TAGLIST_SIZE ((int)(sizeof(taglist) / sizeof(taglist[0])))
|
#define TAGLIST_SIZE ((int)(sizeof(taglist) / sizeof(taglist[0])))
|
||||||
|
|
@ -327,12 +363,12 @@ static const struct tag_resolver taglist[] = {
|
||||||
string. If it is, we attempt to convert it to a 8-bit ASCII string
|
string. If it is, we attempt to convert it to a 8-bit ASCII string
|
||||||
(for valid 8-bit ASCII characters). If it's not unicode, we leave
|
(for valid 8-bit ASCII characters). If it's not unicode, we leave
|
||||||
it alone. At some point we should fully support unicode strings */
|
it alone. At some point we should fully support unicode strings */
|
||||||
static int unicode_munge(char** string, int *len) {
|
static int unicode_munge(char* string, int *len) {
|
||||||
long tmp;
|
long tmp;
|
||||||
bool le = false;
|
bool le = false;
|
||||||
int i;
|
int i;
|
||||||
char *str = *string;
|
char *str = string;
|
||||||
char *outstr = *string;
|
char *outstr = string;
|
||||||
bool bom = false;
|
bool bom = false;
|
||||||
int outlen;
|
int outlen;
|
||||||
|
|
||||||
|
|
@ -343,8 +379,14 @@ static int unicode_munge(char** string, int *len) {
|
||||||
|
|
||||||
/* Type 0x00 is ordinary ISO 8859-1 */
|
/* Type 0x00 is ordinary ISO 8859-1 */
|
||||||
if(str[0] == 0x00) {
|
if(str[0] == 0x00) {
|
||||||
(*len)--;
|
int i = --(*len);
|
||||||
(*string)++; /* Skip the encoding type byte */
|
|
||||||
|
/* We must move the string to the left */
|
||||||
|
while (i--) {
|
||||||
|
string[0] = string[1];
|
||||||
|
string++;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,53 +394,59 @@ static int unicode_munge(char** string, int *len) {
|
||||||
if(str[0] == 0x01 || str[0] == 0x02) {
|
if(str[0] == 0x01 || str[0] == 0x02) {
|
||||||
(*len)--;
|
(*len)--;
|
||||||
str++;
|
str++;
|
||||||
tmp = BYTES2INT(0, 0, str[0], str[1]);
|
|
||||||
|
|
||||||
/* Now check if there is a BOM (zero-width non-breaking space, 0xfeff)
|
|
||||||
and if it is in little or big endian format */
|
|
||||||
if(tmp == 0xfffe) { /* Little endian? */
|
|
||||||
bom = true;
|
|
||||||
le = true;
|
|
||||||
str += 2;
|
|
||||||
(*len)-=2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(tmp == 0xfeff) { /* Big endian? */
|
|
||||||
bom = true;
|
|
||||||
str += 2;
|
|
||||||
(*len)-=2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If there is no BOM (which is a specification violation),
|
|
||||||
let's try to guess it. If one of the bytes is 0x00, it is
|
|
||||||
probably the most significant one. */
|
|
||||||
if(!bom) {
|
|
||||||
if(str[1] == 0)
|
|
||||||
le = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
|
|
||||||
outlen = *len / 2;
|
/* Handle frames with more than one string (needed for TXXX frames).
|
||||||
|
*/
|
||||||
do {
|
do {
|
||||||
if(le) {
|
tmp = BYTES2INT(0, 0, str[0], str[1]);
|
||||||
if(str[1])
|
|
||||||
outstr[i++] = '.';
|
/* Now check if there is a BOM (zero-width non-breaking space, 0xfeff)
|
||||||
else
|
and if it is in little or big endian format */
|
||||||
outstr[i++] = str[0];
|
if(tmp == 0xfffe) { /* Little endian? */
|
||||||
} else {
|
bom = true;
|
||||||
if(str[0])
|
le = true;
|
||||||
outstr[i++] = '.';
|
str += 2;
|
||||||
else
|
(*len)-=2;
|
||||||
outstr[i++] = str[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(tmp == 0xfeff) { /* Big endian? */
|
||||||
|
bom = true;
|
||||||
|
str += 2;
|
||||||
|
(*len)-=2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there is no BOM (which is a specification violation),
|
||||||
|
let's try to guess it. If one of the bytes is 0x00, it is
|
||||||
|
probably the most significant one. */
|
||||||
|
if(!bom) {
|
||||||
|
if(str[1] == 0)
|
||||||
|
le = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
outlen = *len / 2;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(le) {
|
||||||
|
if(str[1])
|
||||||
|
outstr[i++] = '.';
|
||||||
|
else
|
||||||
|
outstr[i++] = str[0];
|
||||||
|
} else {
|
||||||
|
if(str[0])
|
||||||
|
outstr[i++] = '.';
|
||||||
|
else
|
||||||
|
outstr[i++] = str[1];
|
||||||
|
}
|
||||||
|
str += 2;
|
||||||
|
} while((str[0] || str[1]) && (i < outlen));
|
||||||
|
|
||||||
str += 2;
|
str += 2;
|
||||||
} while((str[0] || str[1]) && (i < outlen));
|
outstr[i++] = 0; /* Terminate the string */
|
||||||
|
} while(i < outlen);
|
||||||
|
|
||||||
*len = i;
|
*len = i - 1;
|
||||||
|
|
||||||
outstr[i] = 0; /* Terminate the string */
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -686,29 +734,33 @@ static void setid3v2title(int fd, struct mp3entry *entry)
|
||||||
|
|
||||||
for (i=0; i<TAGLIST_SIZE; i++) {
|
for (i=0; i<TAGLIST_SIZE; i++) {
|
||||||
const struct tag_resolver* tr = &taglist[i];
|
const struct tag_resolver* tr = &taglist[i];
|
||||||
char** ptag = (char**) (((char*)entry) + tr->offset);
|
char** ptag = tr->offset ? (char**) (((char*)entry) + tr->offset)
|
||||||
|
: NULL;
|
||||||
char* tag;
|
char* tag;
|
||||||
|
|
||||||
if( !*ptag && !memcmp( header, tr->tag, tr->tag_length ) ) {
|
if( (!ptag || !*ptag) && !memcmp( header, tr->tag, tr->tag_length ) ) {
|
||||||
|
|
||||||
/* found a tag matching one in tagList, and not yet filled */
|
/* found a tag matching one in tagList, and not yet filled */
|
||||||
|
tag = buffer + bufferpos;
|
||||||
|
|
||||||
if(global_unsynch && version <= ID3_VER_2_3)
|
if(global_unsynch && version <= ID3_VER_2_3)
|
||||||
bytesread = read_unsynched(fd, buffer + bufferpos,
|
bytesread = read_unsynched(fd, tag, framelen);
|
||||||
framelen);
|
|
||||||
else
|
else
|
||||||
bytesread = read(fd, buffer + bufferpos, framelen);
|
bytesread = read(fd, tag, framelen);
|
||||||
|
|
||||||
if( bytesread != framelen )
|
if( bytesread != framelen )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
size -= bytesread;
|
size -= bytesread;
|
||||||
*ptag = buffer + bufferpos;
|
|
||||||
|
|
||||||
if(unsynch || (global_unsynch && version >= ID3_VER_2_4))
|
|
||||||
bytesread = unsynchronize_frame(*ptag, bytesread);
|
|
||||||
|
|
||||||
unicode_munge( ptag, &bytesread );
|
if(unsynch || (global_unsynch && version >= ID3_VER_2_4))
|
||||||
tag = *ptag;
|
bytesread = unsynchronize_frame(tag, bytesread);
|
||||||
|
|
||||||
|
unicode_munge( tag, &bytesread );
|
||||||
|
|
||||||
|
if (ptag)
|
||||||
|
*ptag = tag;
|
||||||
|
|
||||||
/* remove trailing spaces */
|
/* remove trailing spaces */
|
||||||
while ( bytesread > 0 && isspace(tag[bytesread-1]))
|
while ( bytesread > 0 && isspace(tag[bytesread-1]))
|
||||||
bytesread--;
|
bytesread--;
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,12 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
#include <string.h>
|
||||||
|
#include <system.h>
|
||||||
|
#include "id3.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
/* The fixed point math routines (with the exception of fp_atof) are based
|
/* The fixed point math routines (with the exception of fp_atof) are based
|
||||||
|
|
@ -326,3 +329,106 @@ long get_replaypeak(const char* str)
|
||||||
|
|
||||||
return peak;
|
return peak;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Compare two strings, ignoring case, up to the end nil or another end of
|
||||||
|
* string character. E.g., if eos is '=', "a=" would equal "a". Returns
|
||||||
|
* true for a match, false otherwise.
|
||||||
|
* TODO: This should be placed somewhere else, as it could be useful in
|
||||||
|
* other places too.
|
||||||
|
*/
|
||||||
|
static bool str_equal(const char* s1, const char* s2, char eos)
|
||||||
|
{
|
||||||
|
char c1 = 0;
|
||||||
|
char c2 = 0;
|
||||||
|
|
||||||
|
while (*s1 && *s2 && (*s1 != eos) && (*s2 != eos))
|
||||||
|
{
|
||||||
|
if ((c1 = toupper(*s1)) != (c2 = toupper(*s2)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s1++;
|
||||||
|
s2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c1 == eos)
|
||||||
|
{
|
||||||
|
c1 = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c2 == eos)
|
||||||
|
{
|
||||||
|
c2 = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return c1 == c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for a ReplayGain tag conforming to the "VorbisGain standard". If
|
||||||
|
* found, set the mp3entry accordingly. If value is NULL, key is expected
|
||||||
|
* to be on the "key=value" format, and the comparion/extraction is done
|
||||||
|
* accordingly. buffer is where to store the text contents of the gain tags;
|
||||||
|
* up to length bytes (including end nil) can be written.
|
||||||
|
* Returns number of bytes written to the tag text buffer, or zero if
|
||||||
|
* no ReplayGain tag was found (or nothing was copied to the buffer for
|
||||||
|
* other reasons).
|
||||||
|
*/
|
||||||
|
long parse_replaygain(const char* key, const char* value,
|
||||||
|
struct mp3entry* entry, char* buffer, int length)
|
||||||
|
{
|
||||||
|
const char* val = value;
|
||||||
|
char **p = NULL;
|
||||||
|
char eos = '\0';
|
||||||
|
|
||||||
|
if (!val)
|
||||||
|
{
|
||||||
|
if (!(val = strchr(key, '=')))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
val++;
|
||||||
|
eos = '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_equal(key, "replaygain_track_gain", eos)
|
||||||
|
|| (str_equal(key, "rg_radio", eos) && !entry->track_gain))
|
||||||
|
{
|
||||||
|
entry->track_gain = get_replaygain(val);
|
||||||
|
p = &(entry->track_gain_string);
|
||||||
|
}
|
||||||
|
else if (str_equal(key, "replaygain_album_gain", eos)
|
||||||
|
|| (str_equal(key, "rg_audiophile", eos) && !entry->album_gain))
|
||||||
|
{
|
||||||
|
entry->album_gain = get_replaygain(val);
|
||||||
|
p = &(entry->album_gain_string);
|
||||||
|
}
|
||||||
|
else if (str_equal(key, "replaygain_track_peak", eos)
|
||||||
|
|| (str_equal(key, "rg_peak", eos) && !entry->track_peak))
|
||||||
|
{
|
||||||
|
entry->track_peak = get_replaypeak(val);
|
||||||
|
}
|
||||||
|
else if (str_equal(key, "replaygain_album_peak", eos))
|
||||||
|
{
|
||||||
|
entry->album_peak = get_replaypeak(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p)
|
||||||
|
{
|
||||||
|
int len = strlen(val);
|
||||||
|
|
||||||
|
len = MIN(len, length - 1);
|
||||||
|
|
||||||
|
/* A few characters just isn't interesting... */
|
||||||
|
if (len > 1)
|
||||||
|
{
|
||||||
|
strncpy(buffer, val, len);
|
||||||
|
buffer[len] = 0;
|
||||||
|
*p = buffer;
|
||||||
|
return len + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue