summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/metadata.c292
1 files changed, 292 insertions, 0 deletions
diff --git a/apps/metadata.c b/apps/metadata.c
index 2e39ab1685..540e62c4aa 100644
--- a/apps/metadata.c
+++ b/apps/metadata.c
@@ -25,6 +25,7 @@
25#include "mp3_playback.h" 25#include "mp3_playback.h"
26#include "mp3data.h" 26#include "mp3data.h"
27#include "logf.h" 27#include "logf.h"
28#include "atoi.h"
28 29
29/* Simple file type probing by looking filename extension. */ 30/* Simple file type probing by looking filename extension. */
30int probe_file_format(const char *filename) 31int probe_file_format(const char *filename)
@@ -85,6 +86,8 @@ unsigned short a52_441framesizes[]=
85/* Get metadata for track - return false if parsing showed problems with the 86/* Get metadata for track - return false if parsing showed problems with the
86 file that would prevent playback. */ 87 file that would prevent playback. */
87 88
89static bool get_apetag_info (struct mp3entry *entry, int fd);
90
88bool get_metadata(struct track_info* track, int fd, const char* trackname, 91bool get_metadata(struct track_info* track, int fd, const char* trackname,
89 bool v1first) { 92 bool v1first) {
90 unsigned long totalsamples,bytespersample,channels,bitspersample,numbytes; 93 unsigned long totalsamples,bytespersample,channels,bitspersample,numbytes;
@@ -341,6 +344,7 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
341 (track->id3.length / 8); 344 (track->id3.length / 8);
342 } 345 }
343 346
347 get_apetag_info (&track->id3, fd); /* use any apetag info we find */
344 lseek (fd, 0, SEEK_SET); 348 lseek (fd, 0, SEEK_SET);
345 strncpy (track->id3.path, trackname, sizeof (track->id3.path)); 349 strncpy (track->id3.path, trackname, sizeof (track->id3.path));
346 track->taginfo_ready = true; 350 track->taginfo_ready = true;
@@ -411,3 +415,291 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
411 415
412 return true; 416 return true;
413} 417}
418
419/************************* APE TAG HANDLING CODE ****************************/
420
421/*
422 * This is a first pass at APEv2 tag handling. I'm not sure if this should
423 * reside here, but I wanted to modify as little as possible since I don't
424 * have a feel for the complete system. It may be that APEv2 tags should be
425 * added to the ID3 handling code in the firmware directory. APEv2 tags are
426 * used in WavPack files and Musepack files by default, however they are
427 * also used in MP3 files sometimes (by Foobar2000). Also, WavPack files can
428 * also use ID3v1 tags (but not ID3v2), so it seems like some universal tag
429 * handler might be a reasonable approach.
430 *
431 * This code does not currently handle APEv1 tags, but I believe that this
432 * is not a problem because they were only used in Monkey's Audio files which
433 * will probably never be playable in RockBox (and certainly not by this CPU).
434 */
435
436#define APETAG_HEADER_FORMAT "8LLLL"
437#define APETAG_HEADER_LENGTH 32
438#define APETAG_DATA_LIMIT 4096
439
440struct apetag_header {
441 char id [8];
442 long version, length, item_count, flags;
443 char res [8];
444};
445
446static struct apetag {
447 struct apetag_header header;
448 char data [APETAG_DATA_LIMIT];
449} temp_apetag;
450
451static int get_apetag_item (struct apetag *tag,
452 const char *item,
453 char *value,
454 int size);
455
456static int load_apetag (int fd, struct apetag *tag);
457static void UTF8ToAnsi (unsigned char *pUTF8);
458
459/*
460 * This function searches the specified file for an APEv2 tag and uses any
461 * information found there to populate the appropriate fields in the specified
462 * mp3entry structure. A temporary buffer is used to hold the tag during this
463 * operation. For now, the actual string data that needs to be held during the
464 * life of the track entry is stored in the "id3v2buf" field (which should not
465 * be used for any file that has an APEv2 tag). This limits the total space
466 * for the artist, title, album, composer and genre strings to 300 characters.
467 */
468
469static bool get_apetag_info (struct mp3entry *entry, int fd)
470{
471 int rem_space = sizeof (entry->id3v2buf), str_space;
472 char *temp_buffer = entry->id3v2buf;
473
474 if (rem_space <= 1 || !load_apetag (fd, &temp_apetag))
475 return false;
476
477 if (get_apetag_item (&temp_apetag, "year", temp_buffer, rem_space))
478 entry->year = atoi (temp_buffer);
479
480 if (get_apetag_item (&temp_apetag, "track", temp_buffer, rem_space))
481 entry->tracknum = atoi (temp_buffer);
482
483 if (get_apetag_item (&temp_apetag, "artist", temp_buffer, rem_space)) {
484 UTF8ToAnsi (entry->artist = temp_buffer);
485 str_space = strlen (temp_buffer) + 1;
486 temp_buffer += str_space;
487 rem_space -= str_space;
488 }
489
490 if (rem_space > 1 &&
491 get_apetag_item (&temp_apetag, "title", temp_buffer, rem_space)) {
492 UTF8ToAnsi (entry->title = temp_buffer);
493 str_space = strlen (temp_buffer) + 1;
494 temp_buffer += str_space;
495 rem_space -= str_space;
496 }
497
498 if (rem_space > 1 &&
499 get_apetag_item (&temp_apetag, "album", temp_buffer, rem_space)) {
500 UTF8ToAnsi (entry->album = temp_buffer);
501 str_space = strlen (temp_buffer) + 1;
502 temp_buffer += str_space;
503 rem_space -= str_space;
504 }
505
506 if (rem_space > 1 &&
507 get_apetag_item (&temp_apetag, "genre", temp_buffer, rem_space)) {
508 UTF8ToAnsi (entry->genre_string = temp_buffer);
509 str_space = strlen (temp_buffer) + 1;
510 temp_buffer += str_space;
511 rem_space -= str_space;
512 }
513
514 if (rem_space > 1 &&
515 get_apetag_item (&temp_apetag, "composer", temp_buffer, rem_space))
516 UTF8ToAnsi (entry->composer = temp_buffer);
517
518 return true;
519}
520
521/*
522 * Helper function to convert little-endian structures to easily usable native
523 * format using a format string (this does nothing on a little-endian machine).
524 */
525
526static void little_endian_to_native (void *data, char *format)
527{
528 unsigned char *cp = (unsigned char *) data;
529 long temp;
530
531 while (*format) {
532 switch (*format) {
533 case 'L':
534 temp = cp [0] + ((long) cp [1] << 8) + ((long) cp [2] << 16) + ((long) cp [3] << 24);
535 * (long *) cp = temp;
536 cp += 4;
537 break;
538
539 case 'S':
540 temp = cp [0] + (cp [1] << 8);
541 * (short *) cp = (short) temp;
542 cp += 2;
543 break;
544
545 default:
546 if (*format >= '0' && *format <= '9')
547 cp += *format - '0';
548
549 break;
550 }
551
552 format++;
553 }
554}
555
556/*
557 * Attempt to obtain the named string-type item from the specified APEv2 tag.
558 * The tag value will be copied to "value" (including an appended terminating
559 * NULL) and the length of the string (including the NULL) will be returned.
560 * If the data will not fit in the specified "size" then it will be truncated
561 * early (but still terminated). If the specified item is not found then 0 is
562 * returned and written to the first character of "value". If "value" is
563 * passed in as NULL, then the specified size is ignored and the actual size
564 * required to store the value is returned.
565 *
566 * Note that this function does not work on binary tag data; only UTF-8
567 * encoded strings. However, numeric data (like ReplayGain) is usually stored
568 * as strings.
569 *
570 * Also, APEv2 tags may have multiple values for a given item and these will
571 * all be copied to "value" with NULL separators (this is why the total data
572 * size is returned). Of course, it is possible to ignore any additional
573 * values by simply using up to the first NULL.
574 */
575
576static int get_apetag_item (struct apetag *tag,
577 const char *item,
578 char *value,
579 int size)
580{
581 if (value && size)
582 *value = 0;
583
584 if (tag->header.id [0] == 'A') {
585 char *p = tag->data;
586 char *q = p + tag->header.length - APETAG_HEADER_LENGTH;
587 int i;
588
589 for (i = 0; i < tag->header.item_count; ++i) {
590 int vsize, flags, isize;
591
592 vsize = * (long *) p; p += 4;
593 flags = * (long *) p; p += 4;
594 isize = strlen (p);
595
596 little_endian_to_native (&vsize, "L");
597 little_endian_to_native (&flags, "L");
598
599 if (p + isize + vsize + 1 > q)
600 break;
601
602 if (isize && vsize && !stricmp (item, p) && !(flags & 6)) {
603
604 if (value) {
605 if (vsize + 1 > size)
606 vsize = size - 1;
607
608 memcpy (value, p + isize + 1, vsize);
609 value [vsize] = 0;
610 }
611
612 return vsize + 1;
613 }
614 else
615 p += isize + vsize + 1;
616 }
617 }
618
619 return 0;
620}
621
622/*
623 * Attempt to load an APEv2 tag from the specified file into the specified
624 * structure. If the APEv2 tag will not fit into the predefined data size,
625 * then the tag is not loaded. A return value of TRUE indicates success.
626 */
627
628static int load_apetag (int fd, struct apetag *tag)
629{
630 if (lseek (fd, -APETAG_HEADER_LENGTH, SEEK_END) == -1 ||
631 read (fd, &tag->header, APETAG_HEADER_LENGTH) != APETAG_HEADER_LENGTH ||
632 strncmp (tag->header.id, "APETAGEX", 8)) {
633 tag->header.id [0] = 0;
634 return false;
635 }
636
637 little_endian_to_native (&tag->header, APETAG_HEADER_FORMAT);
638
639 if (tag->header.version == 2000 && tag->header.item_count &&
640 tag->header.length > APETAG_HEADER_LENGTH &&
641 tag->header.length < APETAG_DATA_LIMIT) {
642
643 int data_size = tag->header.length - APETAG_HEADER_LENGTH;
644
645 if (lseek (fd, -tag->header.length, SEEK_END) == -1 ||
646 read (fd, tag->data, data_size) != data_size) {
647 tag->header.id [0] = 0;
648 return false;
649 }
650 else
651 return true;
652 }
653
654 tag->header.id [0] = 0;
655 return false;
656}
657
658/*
659 * This is a *VERY* boneheaded attempt to convert UTF-8 unicode character
660 * strings to ANSI. It simply maps the 16-bit Unicode characters that are
661 * less than 0x100 directly to an 8-bit value, and turns all the rest into
662 * question marks. This can be done "in-place" because the resulting string
663 * can only get smaller.
664 */
665
666static void UTF8ToAnsi (unsigned char *pUTF8)
667{
668 unsigned char *pAnsi = pUTF8;
669 unsigned short widechar = 0;
670 int trail_bytes = 0;
671
672 while (*pUTF8) {
673 if (*pUTF8 & 0x80) {
674 if (*pUTF8 & 0x40) {
675 if (trail_bytes) {
676 trail_bytes = 0;
677 *pAnsi++ = widechar < 0x100 ? widechar : '?';
678 }
679 else {
680 char temp = *pUTF8;
681
682 while (temp & 0x80) {
683 trail_bytes++;
684 temp <<= 1;
685 }
686
687 widechar = temp >> trail_bytes--;
688 }
689 }
690 else if (trail_bytes) {
691 widechar = (widechar << 6) | (*pUTF8 & 0x3f);
692
693 if (!--trail_bytes)
694 *pAnsi++ = widechar < 0x100 ? widechar : '?';
695 }
696 }
697 else
698 *pAnsi++ = *pUTF8;
699
700 pUTF8++;
701 }
702
703 *pAnsi = 0;
704}
705