diff options
-rw-r--r-- | apps/codecs/mpa.c | 3 | ||||
-rw-r--r-- | apps/metadata.c | 38 | ||||
-rw-r--r-- | apps/screens.c | 8 | ||||
-rw-r--r-- | firmware/export/id3.h | 4 | ||||
-rw-r--r-- | firmware/export/replaygain.h | 4 | ||||
-rw-r--r-- | firmware/id3.c | 166 | ||||
-rw-r--r-- | firmware/replaygain.c | 108 |
7 files changed, 235 insertions, 96 deletions
diff --git a/apps/codecs/mpa.c b/apps/codecs/mpa.c index 2dcc71922e..fb478222a4 100644 --- a/apps/codecs/mpa.c +++ b/apps/codecs/mpa.c | |||
@@ -118,6 +118,7 @@ enum codec_status codec_start(struct codec_api* api) | |||
118 | frequency_divider = 441; | 118 | frequency_divider = 441; |
119 | 119 | ||
120 | ci->configure(DSP_SET_FREQUENCY, (int *)ci->id3->frequency); | 120 | ci->configure(DSP_SET_FREQUENCY, (int *)ci->id3->frequency); |
121 | codec_set_replaygain(ci->id3); | ||
121 | 122 | ||
122 | ci->request_buffer(&size, ci->id3->first_frame_offset); | 123 | ci->request_buffer(&size, ci->id3->first_frame_offset); |
123 | ci->advance_buffer(size); | 124 | ci->advance_buffer(size); |
@@ -144,7 +145,7 @@ enum codec_status codec_start(struct codec_api* api) | |||
144 | samplecount = ci->id3->length * frequency_divider / 10; | 145 | samplecount = ci->id3->length * frequency_divider / 10; |
145 | samplesdone = ci->id3->elapsed * frequency_divider / 10; | 146 | samplesdone = ci->id3->elapsed * frequency_divider / 10; |
146 | } | 147 | } |
147 | 148 | ||
148 | /* This is the decoding loop. */ | 149 | /* This is the decoding loop. */ |
149 | while (1) { | 150 | while (1) { |
150 | ci->yield(); | 151 | ci->yield(); |
diff --git a/apps/metadata.c b/apps/metadata.c index 409bdf88c5..bbbfe07d66 100644 --- a/apps/metadata.c +++ b/apps/metadata.c | |||
@@ -528,7 +528,7 @@ static bool get_apetag_info (struct mp3entry *entry, int fd) | |||
528 | 528 | ||
529 | if (rem_space > 1 && | 529 | if (rem_space > 1 && |
530 | get_apetag_item (&temp_apetag, "replaygain_track_gain", temp_buffer, rem_space)) { | 530 | get_apetag_item (&temp_apetag, "replaygain_track_gain", temp_buffer, rem_space)) { |
531 | entry->track_gain = get_replaygain (entry->track_gain_str = temp_buffer); | 531 | entry->track_gain = get_replaygain (entry->track_gain_string = temp_buffer); |
532 | str_space = strlen (temp_buffer) + 1; | 532 | str_space = strlen (temp_buffer) + 1; |
533 | temp_buffer += str_space; | 533 | temp_buffer += str_space; |
534 | rem_space -= str_space; | 534 | rem_space -= str_space; |
@@ -536,7 +536,7 @@ static bool get_apetag_info (struct mp3entry *entry, int fd) | |||
536 | 536 | ||
537 | if (rem_space > 1 && | 537 | if (rem_space > 1 && |
538 | get_apetag_item (&temp_apetag, "replaygain_album_gain", temp_buffer, rem_space)) { | 538 | get_apetag_item (&temp_apetag, "replaygain_album_gain", temp_buffer, rem_space)) { |
539 | entry->album_gain = get_replaygain (entry->album_gain_str = temp_buffer); | 539 | entry->album_gain = get_replaygain (entry->album_gain_string = temp_buffer); |
540 | str_space = strlen (temp_buffer) + 1; | 540 | str_space = strlen (temp_buffer) + 1; |
541 | temp_buffer += str_space; | 541 | temp_buffer += str_space; |
542 | rem_space -= str_space; | 542 | rem_space -= str_space; |
@@ -910,37 +910,13 @@ static bool get_vorbis_comments (struct mp3entry *entry, int fd) | |||
910 | } else if (strncasecmp(temp, "TRACKNUMBER=", 12) == 0) { | 910 | } else if (strncasecmp(temp, "TRACKNUMBER=", 12) == 0) { |
911 | name_length = 11; | 911 | name_length = 11; |
912 | p = &(entry->track_string); | 912 | p = &(entry->track_string); |
913 | } else if ((strncasecmp(temp, "RG_RADIO=", 9) == 0) | ||
914 | && !entry->track_gain) { | ||
915 | entry->track_gain = get_replaygain(&temp[9]); | ||
916 | name_length = 8; | ||
917 | p = &(entry->track_gain_str); | ||
918 | } else if (strncasecmp(temp, "REPLAYGAIN_TRACK_GAIN=", 22) == 0) { | ||
919 | entry->track_gain = get_replaygain(&temp[22]); | ||
920 | name_length = 21; | ||
921 | p = &(entry->track_gain_str); | ||
922 | } else if ((strncasecmp(temp, "RG_AUDIOPHILE=", 14) == 0) | ||
923 | && !entry->album_gain) { | ||
924 | entry->album_gain = get_replaygain(&temp[14]); | ||
925 | name_length = 13; | ||
926 | p = &(entry->album_gain_str); | ||
927 | } else if (strncasecmp(temp, "REPLAYGAIN_ALBUM_GAIN=", 22) == 0) { | ||
928 | entry->album_gain = get_replaygain(&temp[22]); | ||
929 | name_length = 21; | ||
930 | p = &(entry->album_gain_str); | ||
931 | } else if ((strncasecmp(temp, "RG_PEAK=", 8) == 0) | ||
932 | && !entry->track_peak) { | ||
933 | entry->track_peak = get_replaypeak(&temp[8]); | ||
934 | p = NULL; | ||
935 | } else if (strncasecmp(temp, "REPLAYGAIN_TRACK_PEAK=", 22) == 0) { | ||
936 | entry->track_peak = get_replaypeak(&temp[22]); | ||
937 | p = NULL; | ||
938 | } else if (strncasecmp(temp, "REPLAYGAIN_ALBUM_PEAK=", 22) == 0) { | ||
939 | entry->album_peak = get_replaypeak(&temp[22]); | ||
940 | p = NULL; | ||
941 | } else { | 913 | } else { |
914 | int value_length = parse_replaygain(temp, NULL, entry, buffer, | ||
915 | buffer_remaining); | ||
916 | buffer_remaining -= value_length; | ||
917 | buffer += value_length; | ||
942 | p = NULL; | 918 | p = NULL; |
943 | } | 919 | } |
944 | 920 | ||
945 | if (p) { | 921 | if (p) { |
946 | comment_length -= (name_length + 1); | 922 | comment_length -= (name_length + 1); |
diff --git a/apps/screens.c b/apps/screens.c index 86d11a73a4..2c7d5fe9b1 100644 --- a/apps/screens.c +++ b/apps/screens.c | |||
@@ -1388,15 +1388,15 @@ bool browse_id3(void) | |||
1388 | #if CONFIG_HWCODEC == MASNONE | 1388 | #if CONFIG_HWCODEC == MASNONE |
1389 | case 11: | 1389 | case 11: |
1390 | lcd_puts(0, 0, str(LANG_ID3_TRACK_GAIN)); | 1390 | lcd_puts(0, 0, str(LANG_ID3_TRACK_GAIN)); |
1391 | lcd_puts(0, 1, id3->track_gain_str | 1391 | lcd_puts(0, 1, id3->track_gain_string |
1392 | ? id3->track_gain_str | 1392 | ? id3->track_gain_string |
1393 | : (char*) str(LANG_ID3_NO_GAIN)); | 1393 | : (char*) str(LANG_ID3_NO_GAIN)); |
1394 | break; | 1394 | break; |
1395 | 1395 | ||
1396 | case 12: | 1396 | case 12: |
1397 | lcd_puts(0, 0, str(LANG_ID3_ALBUM_GAIN)); | 1397 | lcd_puts(0, 0, str(LANG_ID3_ALBUM_GAIN)); |
1398 | lcd_puts(0, 1, id3->album_gain_str | 1398 | lcd_puts(0, 1, id3->album_gain_string |
1399 | ? id3->album_gain_str | 1399 | ? id3->album_gain_string |
1400 | : (char*) str(LANG_ID3_NO_GAIN)); | 1400 | : (char*) str(LANG_ID3_NO_GAIN)); |
1401 | break; | 1401 | break; |
1402 | #endif | 1402 | #endif |
diff --git a/firmware/export/id3.h b/firmware/export/id3.h index 348b17e191..abb354b233 100644 --- a/firmware/export/id3.h +++ b/firmware/export/id3.h | |||
@@ -119,8 +119,8 @@ struct mp3entry { | |||
119 | /* replaygain support */ | 119 | /* replaygain support */ |
120 | 120 | ||
121 | #if CONFIG_HWCODEC == MASNONE | 121 | #if CONFIG_HWCODEC == MASNONE |
122 | char* track_gain_str; | 122 | char* track_gain_string; |
123 | char* album_gain_str; | 123 | char* album_gain_string; |
124 | long track_gain; /* 7.24 signed fixed point. 0 for no gain. */ | 124 | long track_gain; /* 7.24 signed fixed point. 0 for no gain. */ |
125 | long album_gain; | 125 | long album_gain; |
126 | long track_peak; /* 7.24 signed fixed point. 0 for no peak. */ | 126 | long track_peak; /* 7.24 signed fixed point. 0 for no peak. */ |
diff --git a/firmware/export/replaygain.h b/firmware/export/replaygain.h index 09b0776069..e96a7f907a 100644 --- a/firmware/export/replaygain.h +++ b/firmware/export/replaygain.h | |||
@@ -20,7 +20,11 @@ | |||
20 | #ifndef _REPLAYGAIN_H | 20 | #ifndef _REPLAYGAIN_H |
21 | #define _REPLAYGAIN_H | 21 | #define _REPLAYGAIN_H |
22 | 22 | ||
23 | #include "id3.h" | ||
24 | |||
23 | long get_replaygain(const char* str); | 25 | long get_replaygain(const char* str); |
24 | long get_replaypeak(const char* str); | 26 | long get_replaypeak(const char* str); |
27 | long parse_replaygain(const char* key, const char* value, | ||
28 | struct mp3entry* entry, char* buffer, int length); | ||
25 | 29 | ||
26 | #endif | 30 | #endif |
diff --git a/firmware/id3.c b/firmware/id3.c index 7344bf53c7..d783053426 100644 --- a/firmware/id3.c +++ b/firmware/id3.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include "id3.h" | 41 | #include "id3.h" |
42 | #include "mp3data.h" | 42 | #include "mp3data.h" |
43 | #include "system.h" | 43 | #include "system.h" |
44 | #include "replaygain.h" | ||
44 | 45 | ||
45 | #define UNSYNC(b0,b1,b2,b3) (((long)(b0 & 0x7F) << (3*7)) | \ | 46 | #define UNSYNC(b0,b1,b2,b3) (((long)(b0 & 0x7F) << (3*7)) | \ |
46 | ((long)(b1 & 0x7F) << (2*7)) | \ | 47 | ((long)(b1 & 0x7F) << (2*7)) | \ |
@@ -163,6 +164,10 @@ char* id3_get_codec(const struct mp3entry* id3) | |||
163 | Many ID3 symbolic names come in more than one form. You can add both | 164 | Many ID3 symbolic names come in more than one form. You can add both |
164 | forms, each referencing the same variable in struct mp3entry. | 165 | forms, each referencing the same variable in struct mp3entry. |
165 | If both forms are present, the last found will be used. | 166 | If both forms are present, the last found will be used. |
167 | Note that the offset can be zero, in which case no entry will be set | ||
168 | in the mp3entry struct; the frame is still read into the buffer and | ||
169 | the special processing function is called (several times, if there | ||
170 | are several frames with the same name). | ||
166 | 171 | ||
167 | 4. Alternately, use the TAG_LIST_ENTRY macro with | 172 | 4. Alternately, use the TAG_LIST_ENTRY macro with |
168 | ID3 tag symbolic name, | 173 | ID3 tag symbolic name, |
@@ -305,6 +310,34 @@ static int parsegenre( struct mp3entry* entry, char* tag, int bufferpos ) | |||
305 | } | 310 | } |
306 | } | 311 | } |
307 | 312 | ||
313 | #if CONFIG_HWCODEC == MASNONE | ||
314 | /* parse user defined text, looking for replaygain information. */ | ||
315 | static int parseuser( struct mp3entry* entry, char* tag, int bufferpos ) | ||
316 | { | ||
317 | char* value = NULL; | ||
318 | int desc_len = strlen(tag); | ||
319 | int value_len = 0; | ||
320 | |||
321 | if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) { | ||
322 | /* At least part of the value was read, so we can safely try to | ||
323 | * parse it | ||
324 | */ | ||
325 | |||
326 | value = tag + desc_len + 1; | ||
327 | value_len = parse_replaygain(tag, value, entry, tag, | ||
328 | bufferpos - (tag - entry->id3v2buf)); | ||
329 | } | ||
330 | |||
331 | if (value_len) { | ||
332 | bufferpos = tag - entry->id3v2buf + value_len; | ||
333 | } else { | ||
334 | bufferpos = tag - entry->id3v2buf; | ||
335 | } | ||
336 | |||
337 | return bufferpos; | ||
338 | } | ||
339 | #endif | ||
340 | |||
308 | static const struct tag_resolver taglist[] = { | 341 | static const struct tag_resolver taglist[] = { |
309 | { "TPE1", 4, offsetof(struct mp3entry, artist), NULL }, | 342 | { "TPE1", 4, offsetof(struct mp3entry, artist), NULL }, |
310 | { "TP1", 3, offsetof(struct mp3entry, artist), NULL }, | 343 | { "TP1", 3, offsetof(struct mp3entry, artist), NULL }, |
@@ -319,6 +352,9 @@ static const struct tag_resolver taglist[] = { | |||
319 | { "TCOM", 4, offsetof(struct mp3entry, composer), NULL }, | 352 | { "TCOM", 4, offsetof(struct mp3entry, composer), NULL }, |
320 | { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre }, | 353 | { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre }, |
321 | { "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre }, | 354 | { "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre }, |
355 | #if CONFIG_HWCODEC == MASNONE | ||
356 | { "TXXX", 4, 0, &parseuser }, | ||
357 | #endif | ||
322 | }; | 358 | }; |
323 | 359 | ||
324 | #define TAGLIST_SIZE ((int)(sizeof(taglist) / sizeof(taglist[0]))) | 360 | #define TAGLIST_SIZE ((int)(sizeof(taglist) / sizeof(taglist[0]))) |
@@ -327,12 +363,12 @@ static const struct tag_resolver taglist[] = { | |||
327 | string. If it is, we attempt to convert it to a 8-bit ASCII string | 363 | string. If it is, we attempt to convert it to a 8-bit ASCII string |
328 | (for valid 8-bit ASCII characters). If it's not unicode, we leave | 364 | (for valid 8-bit ASCII characters). If it's not unicode, we leave |
329 | it alone. At some point we should fully support unicode strings */ | 365 | it alone. At some point we should fully support unicode strings */ |
330 | static int unicode_munge(char** string, int *len) { | 366 | static int unicode_munge(char* string, int *len) { |
331 | long tmp; | 367 | long tmp; |
332 | bool le = false; | 368 | bool le = false; |
333 | int i; | 369 | int i; |
334 | char *str = *string; | 370 | char *str = string; |
335 | char *outstr = *string; | 371 | char *outstr = string; |
336 | bool bom = false; | 372 | bool bom = false; |
337 | int outlen; | 373 | int outlen; |
338 | 374 | ||
@@ -343,8 +379,14 @@ static int unicode_munge(char** string, int *len) { | |||
343 | 379 | ||
344 | /* Type 0x00 is ordinary ISO 8859-1 */ | 380 | /* Type 0x00 is ordinary ISO 8859-1 */ |
345 | if(str[0] == 0x00) { | 381 | if(str[0] == 0x00) { |
346 | (*len)--; | 382 | int i = --(*len); |
347 | (*string)++; /* Skip the encoding type byte */ | 383 | |
384 | /* We must move the string to the left */ | ||
385 | while (i--) { | ||
386 | string[0] = string[1]; | ||
387 | string++; | ||
388 | } | ||
389 | |||
348 | return 0; | 390 | return 0; |
349 | } | 391 | } |
350 | 392 | ||
@@ -352,53 +394,59 @@ static int unicode_munge(char** string, int *len) { | |||
352 | if(str[0] == 0x01 || str[0] == 0x02) { | 394 | if(str[0] == 0x01 || str[0] == 0x02) { |
353 | (*len)--; | 395 | (*len)--; |
354 | str++; | 396 | str++; |
355 | tmp = BYTES2INT(0, 0, str[0], str[1]); | ||
356 | |||
357 | /* Now check if there is a BOM (zero-width non-breaking space, 0xfeff) | ||
358 | and if it is in little or big endian format */ | ||
359 | if(tmp == 0xfffe) { /* Little endian? */ | ||
360 | bom = true; | ||
361 | le = true; | ||
362 | str += 2; | ||
363 | (*len)-=2; | ||
364 | } | ||
365 | |||
366 | if(tmp == 0xfeff) { /* Big endian? */ | ||
367 | bom = true; | ||
368 | str += 2; | ||
369 | (*len)-=2; | ||
370 | } | ||
371 | |||
372 | /* If there is no BOM (which is a specification violation), | ||
373 | let's try to guess it. If one of the bytes is 0x00, it is | ||
374 | probably the most significant one. */ | ||
375 | if(!bom) { | ||
376 | if(str[1] == 0) | ||
377 | le = true; | ||
378 | } | ||
379 | |||
380 | i = 0; | 397 | i = 0; |
381 | 398 | ||
382 | outlen = *len / 2; | 399 | /* Handle frames with more than one string (needed for TXXX frames). |
383 | 400 | */ | |
384 | do { | 401 | do { |
385 | if(le) { | 402 | tmp = BYTES2INT(0, 0, str[0], str[1]); |
386 | if(str[1]) | 403 | |
387 | outstr[i++] = '.'; | 404 | /* Now check if there is a BOM (zero-width non-breaking space, 0xfeff) |
388 | else | 405 | and if it is in little or big endian format */ |
389 | outstr[i++] = str[0]; | 406 | if(tmp == 0xfffe) { /* Little endian? */ |
390 | } else { | 407 | bom = true; |
391 | if(str[0]) | 408 | le = true; |
392 | outstr[i++] = '.'; | 409 | str += 2; |
393 | else | 410 | (*len)-=2; |
394 | outstr[i++] = str[1]; | 411 | } |
412 | |||
413 | if(tmp == 0xfeff) { /* Big endian? */ | ||
414 | bom = true; | ||
415 | str += 2; | ||
416 | (*len)-=2; | ||
417 | } | ||
418 | |||
419 | /* If there is no BOM (which is a specification violation), | ||
420 | let's try to guess it. If one of the bytes is 0x00, it is | ||
421 | probably the most significant one. */ | ||
422 | if(!bom) { | ||
423 | if(str[1] == 0) | ||
424 | le = true; | ||
395 | } | 425 | } |
426 | |||
427 | outlen = *len / 2; | ||
428 | |||
429 | do { | ||
430 | if(le) { | ||
431 | if(str[1]) | ||
432 | outstr[i++] = '.'; | ||
433 | else | ||
434 | outstr[i++] = str[0]; | ||
435 | } else { | ||
436 | if(str[0]) | ||
437 | outstr[i++] = '.'; | ||
438 | else | ||
439 | outstr[i++] = str[1]; | ||
440 | } | ||
441 | str += 2; | ||
442 | } while((str[0] || str[1]) && (i < outlen)); | ||
443 | |||
396 | str += 2; | 444 | str += 2; |
397 | } while((str[0] || str[1]) && (i < outlen)); | 445 | outstr[i++] = 0; /* Terminate the string */ |
446 | } while(i < outlen); | ||
398 | 447 | ||
399 | *len = i; | 448 | *len = i - 1; |
400 | 449 | ||
401 | outstr[i] = 0; /* Terminate the string */ | ||
402 | return 0; | 450 | return 0; |
403 | } | 451 | } |
404 | 452 | ||
@@ -686,29 +734,33 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
686 | 734 | ||
687 | for (i=0; i<TAGLIST_SIZE; i++) { | 735 | for (i=0; i<TAGLIST_SIZE; i++) { |
688 | const struct tag_resolver* tr = &taglist[i]; | 736 | const struct tag_resolver* tr = &taglist[i]; |
689 | char** ptag = (char**) (((char*)entry) + tr->offset); | 737 | char** ptag = tr->offset ? (char**) (((char*)entry) + tr->offset) |
738 | : NULL; | ||
690 | char* tag; | 739 | char* tag; |
691 | 740 | ||
692 | if( !*ptag && !memcmp( header, tr->tag, tr->tag_length ) ) { | 741 | if( (!ptag || !*ptag) && !memcmp( header, tr->tag, tr->tag_length ) ) { |
693 | 742 | ||
694 | /* found a tag matching one in tagList, and not yet filled */ | 743 | /* found a tag matching one in tagList, and not yet filled */ |
744 | tag = buffer + bufferpos; | ||
745 | |||
695 | if(global_unsynch && version <= ID3_VER_2_3) | 746 | if(global_unsynch && version <= ID3_VER_2_3) |
696 | bytesread = read_unsynched(fd, buffer + bufferpos, | 747 | bytesread = read_unsynched(fd, tag, framelen); |
697 | framelen); | ||
698 | else | 748 | else |
699 | bytesread = read(fd, buffer + bufferpos, framelen); | 749 | bytesread = read(fd, tag, framelen); |
700 | 750 | ||
701 | if( bytesread != framelen ) | 751 | if( bytesread != framelen ) |
702 | return; | 752 | return; |
703 | 753 | ||
704 | size -= bytesread; | 754 | size -= bytesread; |
705 | *ptag = buffer + bufferpos; | 755 | |
706 | |||
707 | if(unsynch || (global_unsynch && version >= ID3_VER_2_4)) | 756 | if(unsynch || (global_unsynch && version >= ID3_VER_2_4)) |
708 | bytesread = unsynchronize_frame(*ptag, bytesread); | 757 | bytesread = unsynchronize_frame(tag, bytesread); |
709 | 758 | ||
710 | unicode_munge( ptag, &bytesread ); | 759 | unicode_munge( tag, &bytesread ); |
711 | tag = *ptag; | 760 | |
761 | if (ptag) | ||
762 | *ptag = tag; | ||
763 | |||
712 | /* remove trailing spaces */ | 764 | /* remove trailing spaces */ |
713 | while ( bytesread > 0 && isspace(tag[bytesread-1])) | 765 | while ( bytesread > 0 && isspace(tag[bytesread-1])) |
714 | bytesread--; | 766 | bytesread--; |
diff --git a/firmware/replaygain.c b/firmware/replaygain.c index 23a25cc310..542eee6101 100644 --- a/firmware/replaygain.c +++ b/firmware/replaygain.c | |||
@@ -20,9 +20,12 @@ | |||
20 | #include <ctype.h> | 20 | #include <ctype.h> |
21 | #include <inttypes.h> | 21 | #include <inttypes.h> |
22 | #include <math.h> | 22 | #include <math.h> |
23 | #include <stdbool.h> | ||
23 | #include <stdio.h> | 24 | #include <stdio.h> |
24 | #include <stdlib.h> | 25 | #include <stdlib.h> |
25 | #include <stdbool.h> | 26 | #include <string.h> |
27 | #include <system.h> | ||
28 | #include "id3.h" | ||
26 | #include "debug.h" | 29 | #include "debug.h" |
27 | 30 | ||
28 | /* The fixed point math routines (with the exception of fp_atof) are based | 31 | /* The fixed point math routines (with the exception of fp_atof) are based |
@@ -326,3 +329,106 @@ long get_replaypeak(const char* str) | |||
326 | 329 | ||
327 | return peak; | 330 | return peak; |
328 | } | 331 | } |
332 | |||
333 | /* Compare two strings, ignoring case, up to the end nil or another end of | ||
334 | * string character. E.g., if eos is '=', "a=" would equal "a". Returns | ||
335 | * true for a match, false otherwise. | ||
336 | * TODO: This should be placed somewhere else, as it could be useful in | ||
337 | * other places too. | ||
338 | */ | ||
339 | static bool str_equal(const char* s1, const char* s2, char eos) | ||
340 | { | ||
341 | char c1 = 0; | ||
342 | char c2 = 0; | ||
343 | |||
344 | while (*s1 && *s2 && (*s1 != eos) && (*s2 != eos)) | ||
345 | { | ||
346 | if ((c1 = toupper(*s1)) != (c2 = toupper(*s2))) | ||
347 | { | ||
348 | return false; | ||
349 | } | ||
350 | |||
351 | s1++; | ||
352 | s2++; | ||
353 | } | ||
354 | |||
355 | if (c1 == eos) | ||
356 | { | ||
357 | c1 = '\0'; | ||
358 | } | ||
359 | |||
360 | if (c2 == eos) | ||
361 | { | ||
362 | c2 = '\0'; | ||
363 | } | ||
364 | |||
365 | return c1 == c2; | ||
366 | } | ||
367 | |||
368 | /* Check for a ReplayGain tag conforming to the "VorbisGain standard". If | ||
369 | * found, set the mp3entry accordingly. If value is NULL, key is expected | ||
370 | * to be on the "key=value" format, and the comparion/extraction is done | ||
371 | * accordingly. buffer is where to store the text contents of the gain tags; | ||
372 | * up to length bytes (including end nil) can be written. | ||
373 | * Returns number of bytes written to the tag text buffer, or zero if | ||
374 | * no ReplayGain tag was found (or nothing was copied to the buffer for | ||
375 | * other reasons). | ||
376 | */ | ||
377 | long parse_replaygain(const char* key, const char* value, | ||
378 | struct mp3entry* entry, char* buffer, int length) | ||
379 | { | ||
380 | const char* val = value; | ||
381 | char **p = NULL; | ||
382 | char eos = '\0'; | ||
383 | |||
384 | if (!val) | ||
385 | { | ||
386 | if (!(val = strchr(key, '='))) | ||
387 | { | ||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | val++; | ||
392 | eos = '='; | ||
393 | } | ||
394 | |||
395 | if (str_equal(key, "replaygain_track_gain", eos) | ||
396 | || (str_equal(key, "rg_radio", eos) && !entry->track_gain)) | ||
397 | { | ||
398 | entry->track_gain = get_replaygain(val); | ||
399 | p = &(entry->track_gain_string); | ||
400 | } | ||
401 | else if (str_equal(key, "replaygain_album_gain", eos) | ||
402 | || (str_equal(key, "rg_audiophile", eos) && !entry->album_gain)) | ||
403 | { | ||
404 | entry->album_gain = get_replaygain(val); | ||
405 | p = &(entry->album_gain_string); | ||
406 | } | ||
407 | else if (str_equal(key, "replaygain_track_peak", eos) | ||
408 | || (str_equal(key, "rg_peak", eos) && !entry->track_peak)) | ||
409 | { | ||
410 | entry->track_peak = get_replaypeak(val); | ||
411 | } | ||
412 | else if (str_equal(key, "replaygain_album_peak", eos)) | ||
413 | { | ||
414 | entry->album_peak = get_replaypeak(val); | ||
415 | } | ||
416 | |||
417 | if (p) | ||
418 | { | ||
419 | int len = strlen(val); | ||
420 | |||
421 | len = MIN(len, length - 1); | ||
422 | |||
423 | /* A few characters just isn't interesting... */ | ||
424 | if (len > 1) | ||
425 | { | ||
426 | strncpy(buffer, val, len); | ||
427 | buffer[len] = 0; | ||
428 | *p = buffer; | ||
429 | return len + 1; | ||
430 | } | ||
431 | } | ||
432 | |||
433 | return 0; | ||
434 | } | ||