summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
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_ */