summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorNicolas Pennequin <nicolas.pennequin@free.fr>2007-11-11 12:29:37 +0000
committerNicolas Pennequin <nicolas.pennequin@free.fr>2007-11-11 12:29:37 +0000
commit9d4bed7ff06818b098926932db824a8d6532bfee (patch)
tree0a1cfa6a98a75a2badc1a8d2a2d7839454522beb /apps
parentf34720b163aff1d167ae031f23f3250356aa2c1b (diff)
downloadrockbox-9d4bed7ff06818b098926932db824a8d6532bfee.tar.gz
rockbox-9d4bed7ff06818b098926932db824a8d6532bfee.zip
Album art support. Based on FS#3045, but heavily modified to adapt to MoB and for cleanness.
The cover pictures are loaded from external bitmaps. JPEG and embedded art are not supported. The pictures will only be drawn on the main display. There is no resizing but it is possible to specify the WPS bitmap size in the bitmap names (e.g. cover.100x100.bmp). The bitmaps are stored in the main buffer and read directly from there. Currently, duplicate bitmaps will simply be present several times in the buffer, but this will be improved. To enable for a target, #define HAVE_ALBUMART in its config file. For more information, see the wiki page: http://www.rockbox.org/wiki/AlbumArt. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15572 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r--apps/SOURCES3
-rw-r--r--apps/buffering.c47
-rw-r--r--apps/buffering.h2
-rw-r--r--apps/gui/gwps-common.c14
-rw-r--r--apps/gui/gwps.c15
-rw-r--r--apps/gui/gwps.h42
-rw-r--r--apps/gui/wps_parser.c225
-rw-r--r--apps/playback.c33
-rw-r--r--apps/recorder/albumart.c285
-rw-r--r--apps/recorder/albumart.h39
10 files changed, 700 insertions, 5 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index d50da979ad..5e097049c2 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -75,6 +75,9 @@ recorder/bmp.c
75recorder/icons.c 75recorder/icons.c
76recorder/keyboard.c 76recorder/keyboard.c
77recorder/peakmeter.c 77recorder/peakmeter.c
78#ifdef HAVE_ALBUMART
79recorder/albumart.c
80#endif
78#ifdef HAVE_LCD_COLOR 81#ifdef HAVE_LCD_COLOR
79gui/color_picker.c 82gui/color_picker.c
80#endif 83#endif
diff --git a/apps/buffering.c b/apps/buffering.c
index edfb8e758a..3a412680ea 100644
--- a/apps/buffering.c
+++ b/apps/buffering.c
@@ -48,6 +48,7 @@
48#include "playback.h" 48#include "playback.h"
49#include "pcmbuf.h" 49#include "pcmbuf.h"
50#include "buffer.h" 50#include "buffer.h"
51#include "bmp.h"
51 52
52#ifdef SIMULATOR 53#ifdef SIMULATOR
53#define ata_disk_is_active() 1 54#define ata_disk_is_active() 1
@@ -745,7 +746,7 @@ static void shrink_handle(struct memory_handle *h)
745 746
746 if (h->next && h->filerem == 0 && 747 if (h->next && h->filerem == 0 &&
747 (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET || 748 (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET ||
748 h->type == TYPE_IMAGE || h->type == TYPE_CODEC || 749 h->type == TYPE_BITMAP || h->type == TYPE_CODEC ||
749 h->type == TYPE_ATOMIC_AUDIO)) 750 h->type == TYPE_ATOMIC_AUDIO))
750 { 751 {
751 /* metadata handle: we can move all of it */ 752 /* metadata handle: we can move all of it */
@@ -762,11 +763,15 @@ static void shrink_handle(struct memory_handle *h)
762 h->ridx = RINGBUF_ADD(h->ridx, delta); 763 h->ridx = RINGBUF_ADD(h->ridx, delta);
763 h->widx = RINGBUF_ADD(h->widx, delta); 764 h->widx = RINGBUF_ADD(h->widx, delta);
764 765
765 /* when moving a struct mp3entry we need to readjust its pointers. */
766 if (h->type == TYPE_ID3 && h->filesize == sizeof(struct mp3entry)) { 766 if (h->type == TYPE_ID3 && h->filesize == sizeof(struct mp3entry)) {
767 /* when moving an mp3entry we need to readjust its pointers. */
767 adjust_mp3entry((struct mp3entry *)&buffer[h->data], 768 adjust_mp3entry((struct mp3entry *)&buffer[h->data],
768 (void *)&buffer[h->data], 769 (void *)&buffer[h->data],
769 (void *)&buffer[olddata]); 770 (void *)&buffer[olddata]);
771 } else if (h->type == TYPE_BITMAP) {
772 /* adjust the bitmap's pointer */
773 struct bitmap *bmp = (struct bitmap *)&buffer[h->data];
774 bmp->data = &buffer[h->data + sizeof(struct bitmap)];
770 } 775 }
771 } 776 }
772 else 777 else
@@ -814,6 +819,23 @@ static bool fill_buffer(void)
814 } 819 }
815} 820}
816 821
822#ifdef HAVE_LCD_BITMAP
823/* Given a file descriptor to a bitmap file, write the bitmap data to the
824 buffer, with a struct bitmap and the actual data immediately following.
825 Return value is the total size (struct + data). */
826static int load_bitmap(const int fd)
827{
828 int rc;
829 struct bitmap *bmp = (struct bitmap *)&buffer[buf_widx];
830 /* FIXME: alignment may be needed for the data buffer. */
831 bmp->data = &buffer[buf_widx + sizeof(struct bitmap)];
832 bmp->maskdata = NULL;
833 int free = (int)MIN(buffer_len - BUF_USED, buffer_len - buf_widx);
834 rc = read_bmp_fd(fd, bmp, free, FORMAT_ANY|FORMAT_DITHER);
835 return rc + (rc > 0 ? sizeof(struct bitmap) : 0);
836}
837#endif
838
817 839
818/* 840/*
819MAIN BUFFERING API CALLS 841MAIN BUFFERING API CALLS
@@ -858,7 +880,6 @@ int bufopen(const char *file, size_t offset, enum data_type type)
858 } 880 }
859 881
860 strncpy(h->path, file, MAX_PATH); 882 strncpy(h->path, file, MAX_PATH);
861 h->filesize = size;
862 h->filerem = size - offset; 883 h->filerem = size - offset;
863 h->offset = offset; 884 h->offset = offset;
864 h->ridx = buf_widx; 885 h->ridx = buf_widx;
@@ -867,7 +888,25 @@ int bufopen(const char *file, size_t offset, enum data_type type)
867 h->available = 0; 888 h->available = 0;
868 h->type = type; 889 h->type = type;
869 890
870 if (type == TYPE_CUESHEET || type == TYPE_IMAGE) { 891#ifdef HAVE_LCD_BITMAP
892 if (type == TYPE_BITMAP) {
893 /* Bitmap file: we load the data instead of the file */
894 mutex_lock(&llist_mutex); /* Lock because load_bitmap yields */
895 size = load_bitmap(fd);
896 if (size <= 0)
897 return ERR_FILE_ERROR;
898
899 h->filerem = 0;
900 h->available = size;
901 h->widx = buf_widx + size; /* safe because the data doesn't wrap */
902 buf_widx += size; /* safe too */
903 mutex_unlock(&llist_mutex);
904 }
905#endif
906
907 h->filesize = size;
908
909 if (type == TYPE_CUESHEET) {
871 h->fd = fd; 910 h->fd = fd;
872 /* Immediately start buffering those */ 911 /* Immediately start buffering those */
873 LOGFQUEUE("buffering >| Q_BUFFER_HANDLE"); 912 LOGFQUEUE("buffering >| Q_BUFFER_HANDLE");
diff --git a/apps/buffering.h b/apps/buffering.h
index 1d69df2084..06895e7e51 100644
--- a/apps/buffering.h
+++ b/apps/buffering.h
@@ -30,7 +30,7 @@ enum data_type {
30 TYPE_ATOMIC_AUDIO, 30 TYPE_ATOMIC_AUDIO,
31 TYPE_ID3, 31 TYPE_ID3,
32 TYPE_CUESHEET, 32 TYPE_CUESHEET,
33 TYPE_IMAGE, 33 TYPE_BITMAP,
34 TYPE_BUFFER, 34 TYPE_BUFFER,
35 TYPE_UNKNOWN, 35 TYPE_UNKNOWN,
36}; 36};
diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c
index fec13d564f..3c29884260 100644
--- a/apps/gui/gwps-common.c
+++ b/apps/gui/gwps-common.c
@@ -48,6 +48,7 @@
48/* Image stuff */ 48/* Image stuff */
49#include "bmp.h" 49#include "bmp.h"
50#include "atoi.h" 50#include "atoi.h"
51#include "albumart.h"
51#endif 52#endif
52#include "dsp.h" 53#include "dsp.h"
53#include "action.h" 54#include "action.h"
@@ -929,6 +930,19 @@ static char *get_token_value(struct gui_wps *gwps,
929 case WPS_TOKEN_METADATA_COMMENT: 930 case WPS_TOKEN_METADATA_COMMENT:
930 return id3->comment; 931 return id3->comment;
931 932
933#ifdef HAVE_ALBUMART
934 case WPS_TOKEN_ALBUMART_DISPLAY:
935 draw_album_art(gwps, audio_current_aa_hid());
936 return NULL;
937
938 case WPS_TOKEN_ALBUMART_FOUND:
939 if (audio_current_aa_hid() >= 0) {
940 snprintf(buf, buf_size, "C");
941 return buf;
942 }
943 return NULL;
944#endif
945
932 case WPS_TOKEN_FILE_BITRATE: 946 case WPS_TOKEN_FILE_BITRATE:
933 if(id3->bitrate) 947 if(id3->bitrate)
934 snprintf(buf, buf_size, "%d", id3->bitrate); 948 snprintf(buf, buf_size, "%d", id3->bitrate);
diff --git a/apps/gui/gwps.c b/apps/gui/gwps.c
index b7707cdd0d..ad9fce7a8f 100644
--- a/apps/gui/gwps.c
+++ b/apps/gui/gwps.c
@@ -796,3 +796,18 @@ void gui_sync_wps_init(void)
796 unload_remote_wps_backdrop(); 796 unload_remote_wps_backdrop();
797#endif 797#endif
798} 798}
799
800#ifdef HAVE_ALBUMART
801/* Returns true if at least one of the gui_wps screens has an album art
802 tag in its wps structure */
803bool gui_sync_wps_uses_albumart(void)
804{
805 int i;
806 FOR_NB_SCREENS(i) {
807 struct gui_wps *gwps = &gui_wps[i];
808 if (gwps->data && (gwps->data->wps_uses_albumart != WPS_ALBUMART_NONE))
809 return true;
810 }
811 return false;
812}
813#endif
diff --git a/apps/gui/gwps.h b/apps/gui/gwps.h
index 83ff14b80b..391fc72943 100644
--- a/apps/gui/gwps.h
+++ b/apps/gui/gwps.h
@@ -39,6 +39,23 @@
39#define WPS_ALIGN_CENTER 64 39#define WPS_ALIGN_CENTER 64
40#define WPS_ALIGN_LEFT 128 40#define WPS_ALIGN_LEFT 128
41 41
42#ifdef HAVE_ALBUMART
43
44/* albumart definitions */
45#define WPS_ALBUMART_NONE 0 /* WPS does not contain AA tag */
46#define WPS_ALBUMART_CHECK 1 /* WPS contains AA conditional tag */
47#define WPS_ALBUMART_LOAD 2 /* WPS contains AA tag */
48
49#define WPS_ALBUMART_ALIGN_RIGHT WPS_ALIGN_RIGHT /* x align: right */
50#define WPS_ALBUMART_ALIGN_CENTER WPS_ALIGN_CENTER /* x/y align: center */
51#define WPS_ALBUMART_ALIGN_LEFT WPS_ALIGN_LEFT /* x align: left */
52#define WPS_ALBUMART_ALIGN_TOP WPS_ALIGN_RIGHT /* y align: top */
53#define WPS_ALBUMART_ALIGN_BOTTOM WPS_ALIGN_LEFT /* y align: bottom */
54#define WPS_ALBUMART_INCREASE 8 /* increase if smaller */
55#define WPS_ALBUMART_DECREASE 16 /* decrease if larger */
56
57#endif /* HAVE_ALBUMART */
58
42/* wps_data*/ 59/* wps_data*/
43 60
44#ifdef HAVE_LCD_BITMAP 61#ifdef HAVE_LCD_BITMAP
@@ -187,6 +204,12 @@ enum wps_token_type {
187 WPS_TOKEN_IMAGE_DISPLAY, 204 WPS_TOKEN_IMAGE_DISPLAY,
188#endif 205#endif
189 206
207#ifdef HAVE_ALBUMART
208 /* Albumart */
209 WPS_TOKEN_ALBUMART_DISPLAY,
210 WPS_TOKEN_ALBUMART_FOUND,
211#endif
212
190 /* Metadata */ 213 /* Metadata */
191 WPS_TOKEN_METADATA_ARTIST, 214 WPS_TOKEN_METADATA_ARTIST,
192 WPS_TOKEN_METADATA_COMPOSER, 215 WPS_TOKEN_METADATA_COMPOSER,
@@ -309,6 +332,20 @@ struct wps_data
309 short progress_start; 332 short progress_start;
310 short progress_end; 333 short progress_end;
311 bool peak_meter_enabled; 334 bool peak_meter_enabled;
335
336#ifdef HAVE_ALBUMART
337 /* Album art support */
338 unsigned char wps_uses_albumart; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */
339 short albumart_x;
340 short albumart_y;
341 unsigned short albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT,
342 + .._INCREASE, + .._DECREASE */
343 unsigned short albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM,
344 + .._INCREASE, + .._DECREASE */
345 short albumart_max_width;
346 short albumart_max_height;
347#endif
348
312#else /*HAVE_LCD_CHARCELLS */ 349#else /*HAVE_LCD_CHARCELLS */
313 unsigned short wps_progress_pat[8]; 350 unsigned short wps_progress_pat[8];
314 bool full_line_progressbar; 351 bool full_line_progressbar;
@@ -417,4 +454,9 @@ extern struct gui_wps gui_wps[NB_SCREENS];
417void gui_sync_wps_init(void); 454void gui_sync_wps_init(void);
418void gui_sync_wps_screen_init(void); 455void gui_sync_wps_screen_init(void);
419 456
457#ifdef HAVE_ALBUMART
458/* gives back if WPS contains an albumart tag */
459bool gui_sync_wps_uses_albumart(void);
460#endif
461
420#endif 462#endif
diff --git a/apps/gui/wps_parser.c b/apps/gui/wps_parser.c
index 8471bff7d8..097a60c90f 100644
--- a/apps/gui/wps_parser.c
+++ b/apps/gui/wps_parser.c
@@ -113,6 +113,7 @@ static int parse_progressbar(const char *wps_bufptr,
113 struct wps_token *token, struct wps_data *wps_data); 113 struct wps_token *token, struct wps_data *wps_data);
114static int parse_dir_level(const char *wps_bufptr, 114static int parse_dir_level(const char *wps_bufptr,
115 struct wps_token *token, struct wps_data *wps_data); 115 struct wps_token *token, struct wps_data *wps_data);
116
116#ifdef HAVE_LCD_BITMAP 117#ifdef HAVE_LCD_BITMAP
117static int parse_image_special(const char *wps_bufptr, 118static int parse_image_special(const char *wps_bufptr,
118 struct wps_token *token, struct wps_data *wps_data); 119 struct wps_token *token, struct wps_data *wps_data);
@@ -126,6 +127,13 @@ static int parse_image_load(const char *wps_bufptr,
126 struct wps_token *token, struct wps_data *wps_data); 127 struct wps_token *token, struct wps_data *wps_data);
127#endif /*HAVE_LCD_BITMAP */ 128#endif /*HAVE_LCD_BITMAP */
128 129
130#ifdef HAVE_ALBUMART
131static int parse_albumart_load(const char *wps_bufptr,
132 struct wps_token *token, struct wps_data *wps_data);
133static int parse_albumart_conditional(const char *wps_bufptr,
134 struct wps_token *token, struct wps_data *wps_data);
135#endif /* HAVE_ALBUMART */
136
129#ifdef CONFIG_RTC 137#ifdef CONFIG_RTC
130#define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC 138#define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
131#else 139#else
@@ -283,6 +291,11 @@ static const struct wps_tag all_tags[] = {
283 291
284 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load }, 292 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
285 { WPS_TOKEN_IMAGE_PROGRESS_BAR, "P", 0, parse_image_special }, 293 { WPS_TOKEN_IMAGE_PROGRESS_BAR, "P", 0, parse_image_special },
294#ifdef HAVE_ALBUMART
295 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
296 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_DYNAMIC,
297 parse_albumart_conditional },
298#endif
286#if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1)) 299#if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
287 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special }, 300 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
288#endif 301#endif
@@ -606,6 +619,215 @@ static int parse_progressbar(const char *wps_bufptr,
606#endif 619#endif
607} 620}
608 621
622#ifdef HAVE_ALBUMART
623static int parse_albumart_load(const char *wps_bufptr,
624 struct wps_token *token,
625 struct wps_data *wps_data)
626{
627 const char* _pos;
628 bool parsing;
629 const short xalign_mask = WPS_ALBUMART_ALIGN_LEFT |
630 WPS_ALBUMART_ALIGN_CENTER |
631 WPS_ALBUMART_ALIGN_RIGHT;
632 const short yalign_mask = WPS_ALBUMART_ALIGN_TOP |
633 WPS_ALBUMART_ALIGN_CENTER |
634 WPS_ALBUMART_ALIGN_BOTTOM;
635
636 (void)token; /* silence warning */
637
638 /* reset albumart info in wps */
639 wps_data->wps_uses_albumart = WPS_ALBUMART_NONE;
640 wps_data->albumart_max_width = -1;
641 wps_data->albumart_max_height = -1;
642 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
643 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
644
645 /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */
646
647 /* initial validation and parsing of x and y components */
648 if (*wps_bufptr != '|')
649 return 0; /* malformed token: e.g. %Cl7 */
650
651 _pos = wps_bufptr + 1;
652 if (!isdigit(*_pos))
653 return 0; /* malformed token: e.g. %Cl|@ */
654 wps_data->albumart_x = atoi(_pos);
655
656 _pos = strchr(_pos, '|');
657 if (!_pos || !isdigit(*(++_pos)))
658 return 0; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
659
660 wps_data->albumart_y = atoi(_pos);
661
662 _pos = strchr(_pos, '|');
663 if (!_pos)
664 return 0; /* malformed token: no | after y coordinate
665 e.g. %Cl|7|59\n */
666
667 /* parsing width field */
668 parsing = true;
669 while (parsing)
670 {
671 /* apply each modifier in turn */
672 ++_pos;
673 switch (*_pos)
674 {
675 case 'l':
676 case 'L':
677 case '+':
678 wps_data->albumart_xalign =
679 (wps_data->albumart_xalign & xalign_mask) |
680 WPS_ALBUMART_ALIGN_LEFT;
681 break;
682 case 'c':
683 case 'C':
684 wps_data->albumart_xalign =
685 (wps_data->albumart_xalign & xalign_mask) |
686 WPS_ALBUMART_ALIGN_CENTER;
687 break;
688 case 'r':
689 case 'R':
690 case '-':
691 wps_data->albumart_xalign =
692 (wps_data->albumart_xalign & xalign_mask) |
693 WPS_ALBUMART_ALIGN_RIGHT;
694 break;
695 case 'd':
696 case 'D':
697 wps_data->albumart_xalign |= WPS_ALBUMART_DECREASE;
698 break;
699 case 'i':
700 case 'I':
701 wps_data->albumart_xalign |= WPS_ALBUMART_INCREASE;
702 break;
703 case 's':
704 case 'S':
705 wps_data->albumart_xalign |=
706 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
707 break;
708 default:
709 parsing = false;
710 break;
711 }
712 }
713 /* extract max width data */
714 if (*_pos != '|')
715 {
716 if (!isdigit(*_pos))
717 return 0; /* malformed token: e.g. %Cl|7|59|# */
718 wps_data->albumart_max_width = atoi(_pos);
719 _pos = strchr(_pos, '|');
720 if (!_pos)
721 return 0; /* malformed token: no | after width field
722 e.g. %Cl|7|59|200\n */
723 }
724
725 /* parsing height field */
726 parsing = true;
727 while (parsing)
728 {
729 /* apply each modifier in turn */
730 ++_pos;
731 switch (*_pos)
732 {
733 case 't':
734 case 'T':
735 case '-':
736 wps_data->albumart_yalign =
737 (wps_data->albumart_yalign & yalign_mask) |
738 WPS_ALBUMART_ALIGN_TOP;
739 break;
740 case 'c':
741 case 'C':
742 wps_data->albumart_yalign =
743 (wps_data->albumart_yalign & yalign_mask) |
744 WPS_ALBUMART_ALIGN_CENTER;
745 break;
746 case 'b':
747 case 'B':
748 case '+':
749 wps_data->albumart_yalign =
750 (wps_data->albumart_yalign & yalign_mask) |
751 WPS_ALBUMART_ALIGN_BOTTOM;
752 break;
753 case 'd':
754 case 'D':
755 wps_data->albumart_yalign |= WPS_ALBUMART_DECREASE;
756 break;
757 case 'i':
758 case 'I':
759 wps_data->albumart_yalign |= WPS_ALBUMART_INCREASE;
760 break;
761 case 's':
762 case 'S':
763 wps_data->albumart_yalign |=
764 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
765 break;
766 default:
767 parsing = false;
768 break;
769 }
770 }
771 /* extract max height data */
772 if (*_pos != '|')
773 {
774 if (!isdigit(*_pos))
775 return 0; /* malformed token e.g. %Cl|7|59|200|@ */
776 wps_data->albumart_max_height = atoi(_pos);
777 _pos = strchr(_pos, '|');
778 if (!_pos)
779 return 0; /* malformed token: no closing |
780 e.g. %Cl|7|59|200|200\n */
781 }
782
783 /* if we got here, we parsed everything ok .. ! */
784 if (wps_data->albumart_max_width < 0)
785 wps_data->albumart_max_width = 0;
786 else if (wps_data->albumart_max_width > LCD_WIDTH)
787 wps_data->albumart_max_width = LCD_WIDTH;
788
789 if (wps_data->albumart_max_height < 0)
790 wps_data->albumart_max_height = 0;
791 else if (wps_data->albumart_max_height > LCD_HEIGHT)
792 wps_data->albumart_max_height = LCD_HEIGHT;
793
794 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
795
796 /* Skip the rest of the line */
797 return skip_end_of_line(wps_bufptr);
798}
799
800static int parse_albumart_conditional(const char *wps_bufptr,
801 struct wps_token *token,
802 struct wps_data *wps_data)
803{
804 struct wps_token *prevtoken = token;
805 --prevtoken;
806 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
807 {
808 /* This %C is part of a %?C construct.
809 It's either %?C<blah> or %?Cn<blah> */
810 token->type = WPS_TOKEN_ALBUMART_FOUND;
811 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
812 {
813 token->next = true;
814 return 1;
815 }
816 else if (*wps_bufptr == '<')
817 {
818 return 0;
819 }
820 else
821 {
822 token->type = WPS_NO_TOKEN;
823 return 0;
824 }
825 }
826 else
827 return 0;
828};
829#endif /* HAVE_ALBUMART */
830
609/* Parse a generic token from the given string. Return the length read */ 831/* Parse a generic token from the given string. Return the length read */
610static int parse_token(const char *wps_bufptr, struct wps_data *wps_data) 832static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
611{ 833{
@@ -915,6 +1137,9 @@ static void wps_reset(struct wps_data *data)
915 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */ 1137 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
916#endif 1138#endif
917 memset(data, 0, sizeof(*data)); 1139 memset(data, 0, sizeof(*data));
1140#ifdef HAVE_ALBUMART
1141 data->wps_uses_albumart = WPS_ALBUMART_NONE;
1142#endif
918 wps_data_init(data); 1143 wps_data_init(data);
919#ifdef HAVE_REMOTE_LCD 1144#ifdef HAVE_REMOTE_LCD
920 data->remote_wps = rwps; 1145 data->remote_wps = rwps;
diff --git a/apps/playback.c b/apps/playback.c
index 0b2c9bb76c..5c526f94ed 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -66,6 +66,7 @@
66#include "icons.h" 66#include "icons.h"
67#include "peakmeter.h" 67#include "peakmeter.h"
68#include "action.h" 68#include "action.h"
69#include "albumart.h"
69#endif 70#endif
70#include "lang.h" 71#include "lang.h"
71#include "bookmark.h" 72#include "bookmark.h"
@@ -217,6 +218,9 @@ struct track_info {
217 int audio_hid; /* The ID for the track's buffer handle */ 218 int audio_hid; /* The ID for the track's buffer handle */
218 int id3_hid; /* The ID for the track's metadata handle */ 219 int id3_hid; /* The ID for the track's metadata handle */
219 int codec_hid; /* The ID for the track's codec handle */ 220 int codec_hid; /* The ID for the track's codec handle */
221#ifdef HAVE_ALBUMART
222 int aa_hid; /* The ID for the track's album art handle */
223#endif
220 224
221 size_t filesize; /* File total length */ 225 size_t filesize; /* File total length */
222 226
@@ -391,6 +395,15 @@ static bool clear_track_info(struct track_info *track)
391 return false; 395 return false;
392 } 396 }
393 397
398#ifdef HAVE_ALBUMART
399 if (track->aa_hid >= 0) {
400 if (bufclose(track->aa_hid))
401 track->aa_hid = -1;
402 else
403 return false;
404#endif
405 }
406
394 track->filesize = 0; 407 track->filesize = 0;
395 track->taginfo_ready = false; 408 track->taginfo_ready = false;
396 track->event_sent = false; 409 track->event_sent = false;
@@ -635,6 +648,13 @@ void audio_remove_encoder(void)
635 648
636#endif /* HAVE_RECORDING */ 649#endif /* HAVE_RECORDING */
637 650
651#ifdef HAVE_ALBUMART
652int audio_current_aa_hid(void)
653{
654 return CUR_TI->aa_hid;
655}
656#endif
657
638struct mp3entry* audio_current_track(void) 658struct mp3entry* audio_current_track(void)
639{ 659{
640 const char *filename; 660 const char *filename;
@@ -2381,6 +2401,16 @@ static bool audio_load_track(int offset, bool start_play)
2381 else 2401 else
2382 track_id3 = bufgetid3(tracks[track_widx].id3_hid); 2402 track_id3 = bufgetid3(tracks[track_widx].id3_hid);
2383 2403
2404
2405#ifdef HAVE_ALBUMART
2406 if (gui_sync_wps_uses_albumart())
2407 {
2408 char aa_path[MAX_PATH];
2409 if (find_albumart(track_id3, aa_path, sizeof(aa_path)))
2410 tracks[track_widx].aa_hid = bufopen(aa_path, 0, TYPE_BITMAP);
2411 }
2412#endif
2413
2384 track_id3->elapsed = 0; 2414 track_id3->elapsed = 0;
2385 2415
2386 enum data_type type = TYPE_PACKET_AUDIO; 2416 enum data_type type = TYPE_PACKET_AUDIO;
@@ -3286,6 +3316,9 @@ void audio_init(void)
3286 tracks[i].audio_hid = -1; 3316 tracks[i].audio_hid = -1;
3287 tracks[i].id3_hid = -1; 3317 tracks[i].id3_hid = -1;
3288 tracks[i].codec_hid = -1; 3318 tracks[i].codec_hid = -1;
3319#ifdef HAVE_ALBUMART
3320 tracks[i].aa_hid = -1;
3321#endif
3289 } 3322 }
3290 3323
3291 /* Probably safe to say */ 3324 /* Probably safe to say */
diff --git a/apps/recorder/albumart.c b/apps/recorder/albumart.c
new file mode 100644
index 0000000000..abae8c1afc
--- /dev/null
+++ b/apps/recorder/albumart.c
@@ -0,0 +1,285 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Nicolas Pennequin
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include <string.h>
21#include "sprintf.h"
22#include "system.h"
23#include "albumart.h"
24#include "id3.h"
25#include "gwps.h"
26#include "buffering.h"
27#include "dircache.h"
28#include "debug.h"
29
30
31/* Strip filename from a full path
32 *
33 * buf - buffer to extract directory to.
34 * buf_size - size of buffer.
35 * fullpath - fullpath to extract from.
36 *
37 * Split the directory part of the given fullpath and store it in buf
38 * (including last '/').
39 * The function return parameter is a pointer to the filename
40 * inside the given fullpath.
41 */
42static char* strip_filename(char* buf, int buf_size, const char* fullpath)
43{
44 char* sep;
45 int len;
46
47 if (!buf || buf_size <= 0 || !fullpath)
48 return NULL;
49
50 /* if 'fullpath' is only a filename return immediately */
51 sep = strrchr(fullpath, '/');
52 if (sep == NULL)
53 {
54 buf[0] = 0;
55 return (char*)fullpath;
56 }
57
58 len = MIN(sep - fullpath + 1, buf_size - 1);
59 strncpy(buf, fullpath, len);
60 buf[len] = 0;
61 return (sep + 1);
62}
63
64/* Strip extension from a filename.
65 *
66 * buf - buffer to output the result to.
67 * buf_size - size of the output buffer buffer.
68 * file - filename to strip extension from.
69 *
70 * Return value is a pointer to buf, which contains the result.
71 */
72static char* strip_extension(char* buf, int buf_size, const char* file)
73{
74 char* sep;
75 int len;
76
77 if (!buf || buf_size <= 0 || !file)
78 return NULL;
79
80 buf[0] = 0;
81
82 sep = strrchr(file,'.');
83 if (sep == NULL)
84 return NULL;
85
86 len = MIN(sep - file, buf_size - 1);
87 strncpy(buf, file, len);
88 buf[len] = 0;
89 return buf;
90}
91
92/* Test file existence, using dircache of possible */
93static bool file_exists(const char *file)
94{
95 int fd;
96
97 if (!file || strlen(file) <= 0)
98 return false;
99
100#ifdef HAVE_DIRCACHE
101 if (dircache_is_enabled())
102 return (dircache_get_entry_ptr(file) != NULL);
103#endif
104
105 fd = open(file, O_RDONLY);
106 if (fd < 0)
107 return false;
108 close(fd);
109 return true;
110}
111
112/* Look for the first matching album art bitmap in the following list:
113 * ./<trackname><size>.bmp
114 * ./<albumname><size>.bmp
115 * ./cover<size>.bmp
116 * ../<albumname><size>.bmp
117 * ../cover<size>.bmp
118 * <size> is the value of the size_string parameter, <trackname> and
119 * <albumname> are read from the ID3 metadata.
120 * If a matching bitmap is found, its filename is stored in buf.
121 * Return value is true if a bitmap was found, false otherwise.
122 */
123static bool search_files(const struct mp3entry *id3, const char *size_string,
124 char *buf, int buflen)
125{
126 char path[MAX_PATH + 1];
127 char dir[MAX_PATH + 1];
128 bool found = false;
129 const char *trackname;
130
131 if (!id3 || !buf)
132 return false;
133
134 trackname = id3->path;
135 strip_filename(dir, sizeof(dir), trackname);
136
137 /* the first file we look for is one specific to the track playing */
138 strip_extension(path, sizeof(path) - strlen(size_string) - 4, trackname);
139 strcat(path, size_string);
140 strcat(path, ".bmp");
141 found = file_exists(path);
142 if (!found && id3->album && strlen(id3->album) > 0)
143 {
144 /* if it doesn't exist,
145 * we look for a file specific to the track's album name */
146 snprintf(path, sizeof(path) - 1,
147 "%s%s%s.bmp",
148 (strlen(dir) >= 1) ? dir : "",
149 id3->album, size_string);
150 path[sizeof(path) - 1] = 0;
151 found = file_exists(path);
152 }
153
154 if (!found)
155 {
156 /* if it still doesn't exist, we look for a generic file */
157 snprintf(path, sizeof(path)-1,
158 "%scover%s.bmp",
159 (strlen(dir) >= 1) ? dir : "", size_string);
160 path[sizeof(path)-1] = 0;
161 found = file_exists(path);
162 }
163
164 if (!found)
165 {
166 /* if it still doesn't exist,
167 * we continue to search in the parent directory */
168 char temp[MAX_PATH + 1];
169 strncpy(temp, dir, strlen(dir) - 1);
170 temp[strlen(dir) - 1] = 0;
171
172 strip_filename(dir, sizeof(dir), temp);
173 }
174
175 if (!found && id3->album && strlen(id3->album) > 0)
176 {
177 /* we look in the parent directory
178 * for a file specific to the track's album name */
179 snprintf(path, sizeof(path)-1,
180 "%s%s%s.bmp",
181 (strlen(dir) >= 1) ? dir : "",
182 id3->album, size_string);
183 found = file_exists(path);
184 }
185
186 if (!found)
187 {
188 /* if it still doesn't exist, we look in the parent directory
189 * for a generic file */
190 snprintf(path, sizeof(path)-1,
191 "%scover%s.bmp",
192 (strlen(dir) >= 1) ? dir : "", size_string);
193 path[sizeof(path)-1] = 0;
194 found = file_exists(path);
195 }
196
197 if (!found)
198 return false;
199
200 strncpy(buf, path, buflen);
201 DEBUGF("Album art found: %s\n", path);
202 return true;
203}
204
205/* Look for albumart bitmap in the same dir as the track and in its parent dir.
206 * Stores the found filename in the buf parameter.
207 * Returns true if a bitmap was found, false otherwise */
208bool find_albumart(const struct mp3entry *id3, char *buf, int buflen)
209{
210 if (!id3 || !buf)
211 return false;
212
213 char size_string[9];
214 struct wps_data *data = gui_wps[0].data;
215
216 if (!data)
217 return false;
218
219 DEBUGF("Looking for album art for %s\n", id3->path);
220
221 /* Write the size string, e.g. ".100x100". */
222 snprintf(size_string, sizeof(size_string), ".%dx%d",
223 data->albumart_max_width, data->albumart_max_height);
224
225 /* First we look for a bitmap of the right size */
226 if (search_files(id3, size_string, buf, buflen))
227 return true;
228
229 /* Then we look for generic bitmaps */
230 *size_string = 0;
231 return search_files(id3, size_string, buf, buflen);
232}
233
234/* Draw the album art bitmap from the given handle ID onto the given WPS. */
235void draw_album_art(struct gui_wps *gwps, int handle_id)
236{
237 if (!gwps || !gwps->data || !gwps->display || handle_id < 0)
238 return;
239
240 struct wps_data *data = gwps->data;
241
242#ifdef HAVE_REMOTE_LCD
243 /* No album art on RWPS */
244 if (data->remote_wps)
245 return;
246#endif
247
248 struct bitmap *bmp;
249 bufgetdata(handle_id, 0, (void *)&bmp);
250
251 short x = data->albumart_x;
252 short y = data->albumart_y;
253 short width = bmp->width;
254 short height = bmp->height;
255
256 if (data->albumart_max_width > 0)
257 {
258 /* Crop if the bitmap is too wide */
259 width = MIN(bmp->width, data->albumart_max_width);
260
261 /* Align */
262 if (data->albumart_xalign & WPS_ALBUMART_ALIGN_RIGHT)
263 x += data->albumart_max_width - width;
264 else if (data->albumart_xalign & WPS_ALBUMART_ALIGN_CENTER)
265 x += (data->albumart_max_width - width) / 2;
266 }
267
268 if (data->albumart_max_height > 0)
269 {
270 /* Crop if the bitmap is too high */
271 height = MIN(bmp->height, data->albumart_max_height);
272
273 /* Align */
274 if (data->albumart_yalign & WPS_ALBUMART_ALIGN_BOTTOM)
275 y += data->albumart_max_height - height;
276 else if (data->albumart_yalign & WPS_ALBUMART_ALIGN_CENTER)
277 y += (data->albumart_max_height - height) / 2;
278 }
279
280 /* Draw the bitmap */
281 gwps->display->set_drawmode(DRMODE_FG);
282 gwps->display->bitmap_part((fb_data*)bmp->data, 0, 0, bmp->width,
283 x, y, width, height);
284 gwps->display->set_drawmode(DRMODE_SOLID);
285}
diff --git a/apps/recorder/albumart.h b/apps/recorder/albumart.h
new file mode 100644
index 0000000000..21ae50edb9
--- /dev/null
+++ b/apps/recorder/albumart.h
@@ -0,0 +1,39 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Nicolas Pennequin
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#ifndef _ALBUMART_H_
21#define _ALBUMART_H_
22
23#ifdef HAVE_ALBUMART
24
25#include <stdbool.h>
26#include "id3.h"
27#include "gwps.h"
28
29/* Look for albumart bitmap in the same dir as the track and in its parent dir.
30 * Stores the found filename in the buf parameter.
31 * Returns true if a bitmap was found, false otherwise */
32bool find_albumart(const struct mp3entry *id3, char *buf, int buflen);
33
34/* Draw the album art bitmap from the given handle ID onto the given WPS. */
35void draw_album_art(struct gui_wps *gwps, int handle_id);
36
37#endif /* HAVE_ALBUMART */
38
39#endif /* _ALBUMART_H_ */