Index: apps/gui/gwps-common.c =================================================================== --- apps/gui/gwps-common.c (révision 15556) +++ apps/gui/gwps-common.c (copie de travail) @@ -48,6 +48,7 @@ /* Image stuff */ #include "bmp.h" #include "atoi.h" +#include "albumart.h" #endif #include "dsp.h" #include "action.h" @@ -929,6 +930,19 @@ case WPS_TOKEN_METADATA_COMMENT: return id3->comment; +#ifdef HAVE_LCD_BITMAP + case WPS_TOKEN_ALBUMART_DISPLAY: + draw_album_art(gwps, audio_current_aa_hid()); + return NULL; + + case WPS_TOKEN_ALBUMART_FOUND: + if (audio_current_aa_hid() >= 0) { + snprintf(buf, buf_size, "C"); + return buf; + } + return NULL; +#endif + case WPS_TOKEN_FILE_BITRATE: if(id3->bitrate) snprintf(buf, buf_size, "%d", id3->bitrate); Index: apps/gui/gwps.c =================================================================== --- apps/gui/gwps.c (révision 15556) +++ apps/gui/gwps.c (copie de travail) @@ -796,3 +796,18 @@ unload_remote_wps_backdrop(); #endif } + +/* + * returns true if at least one of the gui_wps screens + * has an albumart tag in its wps structure + */ +bool gui_sync_wps_uses_albumart(void) +{ + int i; + FOR_NB_SCREENS(i) { + struct gui_wps *gwps = &gui_wps[i]; + if (gwps->data && (gwps->data->wps_uses_albumart != WPS_ALBUMART_NONE)) + return true; + } + return false; +} Index: apps/gui/gwps.h =================================================================== --- apps/gui/gwps.h (révision 15556) +++ apps/gui/gwps.h (copie de travail) @@ -39,6 +39,19 @@ #define WPS_ALIGN_CENTER 64 #define WPS_ALIGN_LEFT 128 +/* albumart definitions */ +#define WPS_ALBUMART_NONE 0 /* WPS does not contain AA tag */ +#define WPS_ALBUMART_CHECK 1 /* WPS contains AA conditional tag */ +#define WPS_ALBUMART_LOAD 2 /* WPS contains AA tag */ + +#define WPS_ALBUMART_ALIGN_RIGHT WPS_ALIGN_RIGHT /* x align: right */ +#define WPS_ALBUMART_ALIGN_CENTER WPS_ALIGN_CENTER /* x/y align: center */ +#define WPS_ALBUMART_ALIGN_LEFT WPS_ALIGN_LEFT /* x align: left */ +#define WPS_ALBUMART_ALIGN_TOP WPS_ALIGN_RIGHT /* y align: top */ +#define WPS_ALBUMART_ALIGN_BOTTOM WPS_ALIGN_LEFT /* y align: bottom */ +#define WPS_ALBUMART_INCREASE 8 /* increase if smaller */ +#define WPS_ALBUMART_DECREASE 16 /* decrease if larger */ + /* wps_data*/ #ifdef HAVE_LCD_BITMAP @@ -185,6 +198,10 @@ WPS_TOKEN_IMAGE_PRELOAD, WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, WPS_TOKEN_IMAGE_DISPLAY, + + /* Albumart */ + WPS_TOKEN_ALBUMART_DISPLAY, + WPS_TOKEN_ALBUMART_FOUND, #endif /* Metadata */ @@ -309,6 +326,18 @@ short progress_start; short progress_end; bool peak_meter_enabled; + + /* Album art additions */ + unsigned char wps_uses_albumart; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */ + short albumart_x; + short albumart_y; + unsigned short albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT, + + .._INCREASE, + .._DECREASE */ + unsigned short albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM, + + .._INCREASE, + .._DECREASE */ + short albumart_max_width; + short albumart_max_height; + #else /*HAVE_LCD_CHARCELLS */ unsigned short wps_progress_pat[8]; bool full_line_progressbar; @@ -417,4 +446,7 @@ void gui_sync_wps_init(void); void gui_sync_wps_screen_init(void); +/* gives back if WPS contains an albumart tag */ +bool gui_sync_wps_uses_albumart(void); + #endif Index: apps/gui/wps_parser.c =================================================================== --- apps/gui/wps_parser.c (révision 15556) +++ apps/gui/wps_parser.c (copie de travail) @@ -124,6 +124,10 @@ struct wps_token *token, struct wps_data *wps_data); static int parse_image_load(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); +static int parse_albumart_load(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); +static int parse_albumart_conditional(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); #endif /*HAVE_LCD_BITMAP */ #ifdef CONFIG_RTC @@ -283,6 +287,8 @@ { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load }, { WPS_TOKEN_IMAGE_PROGRESS_BAR, "P", 0, parse_image_special }, + { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load }, + { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_DYNAMIC, parse_albumart_conditional }, #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1)) { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special }, #endif @@ -606,6 +612,217 @@ #endif } +#ifdef HAVE_LCD_BITMAP +static int parse_albumart_load(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + const char* _pos; + bool parsing; + const short xalign_mask = WPS_ALBUMART_ALIGN_LEFT | + WPS_ALBUMART_ALIGN_CENTER | + WPS_ALBUMART_ALIGN_RIGHT; + const short yalign_mask = WPS_ALBUMART_ALIGN_TOP | + WPS_ALBUMART_ALIGN_CENTER | + WPS_ALBUMART_ALIGN_BOTTOM; + + (void)(token); /* silence warning */ + + /* reset albumart info in wps */ + wps_data->wps_uses_albumart = WPS_ALBUMART_NONE; + wps_data->albumart_max_width = -1; + wps_data->albumart_max_height = -1; + wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */ + wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */ + + /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */ + /* initial validation and parsing of x and y components */ + if (*wps_bufptr != '|') + return 0; /* malformed token: e.g. %Cl7 */ + + _pos = wps_bufptr+1; + if (!isdigit(*_pos)) + return 0; /* malformed token: e.g. %Cl|@ */ + wps_data->albumart_x = atoi(_pos); + + _pos = strchr(_pos, '|'); + if (!_pos || !isdigit(*(++_pos))) + return 0; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */ + + wps_data->albumart_y = atoi(_pos); + + _pos = strchr(_pos, '|'); + if (!_pos) + return 0; /* malformed token: no | after y coordinate + e.g. %Cl|7|59\n */ + + /* parsing width field */ + parsing = true; + while (parsing) + { + /* apply each modifier in turn */ + ++_pos; + switch (*_pos) + { + case 'l': + case 'L': + case '+': + wps_data->albumart_xalign = + (wps_data->albumart_xalign & xalign_mask) | + WPS_ALBUMART_ALIGN_LEFT; + break; + case 'c': + case 'C': + wps_data->albumart_xalign = + (wps_data->albumart_xalign & xalign_mask) | + WPS_ALBUMART_ALIGN_CENTER; + break; + case 'r': + case 'R': + case '-': + wps_data->albumart_xalign = + (wps_data->albumart_xalign & xalign_mask) | + WPS_ALBUMART_ALIGN_RIGHT; + break; + case 'd': + case 'D': + wps_data->albumart_xalign |= WPS_ALBUMART_DECREASE; + break; + case 'i': + case 'I': + wps_data->albumart_xalign |= WPS_ALBUMART_INCREASE; + break; + case 's': + case 'S': + wps_data->albumart_xalign |= + (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE); + break; + default: + parsing = false; + break; + } + } + /* extract max width data */ + if (*_pos != '|') + { + if (!isdigit(*_pos)) + return 0; /* malformed token: e.g. %Cl|7|59|# */ + wps_data->albumart_max_width = atoi(_pos); + _pos = strchr(_pos, '|'); + if (!_pos) + return 0; /* malformed token: no | after width field + e.g. %Cl|7|59|200\n */ + } + + /* parsing height field */ + parsing = true; + while (parsing) + { + /* apply each modifier in turn */ + ++_pos; + switch (*_pos) + { + case 't': + case 'T': + case 'r': /* yuck */ + case 'R': + case '-': + wps_data->albumart_yalign = + (wps_data->albumart_yalign & yalign_mask) | + WPS_ALBUMART_ALIGN_TOP; + break; + case 'c': + case 'C': + wps_data->albumart_yalign = + (wps_data->albumart_yalign & yalign_mask) | + WPS_ALBUMART_ALIGN_CENTER; + break; + case 'b': + case 'B': + case 'l': /* yuck */ + case 'L': + case '+': + wps_data->albumart_yalign = + (wps_data->albumart_yalign & yalign_mask) | + WPS_ALBUMART_ALIGN_BOTTOM; + break; + case 'd': + case 'D': + wps_data->albumart_yalign |= WPS_ALBUMART_DECREASE; + break; + case 'i': + case 'I': + wps_data->albumart_yalign |= WPS_ALBUMART_INCREASE; + break; + case 's': + case 'S': + wps_data->albumart_yalign |= + (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE); + break; + default: + parsing = false; + break; + } + } + /* extract max height data */ + if (*_pos != '|') + { + if (!isdigit(*_pos)) + return 0; /* malformed token e.g. %Cl|7|59|200|@ */ + wps_data->albumart_max_height = atoi(_pos); + _pos = strchr(_pos, '|'); + if (!_pos) + return 0; /* malformed token: no closing | + e.g. %Cl|7|59|200|200\n */ + } + + /* if we got here, we parsed everything ok .. ! */ + if (wps_data->albumart_max_width < 0) + wps_data->albumart_max_width = 0; + else if (wps_data->albumart_max_width > LCD_WIDTH) + wps_data->albumart_max_width = LCD_WIDTH; + + if (wps_data->albumart_max_height < 0) + wps_data->albumart_max_height = 0; + else if (wps_data->albumart_max_height > LCD_HEIGHT) + wps_data->albumart_max_height = LCD_HEIGHT; + + wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD; + + /* Skip the rest of the line */ + return skip_end_of_line(wps_bufptr); +} + +static int parse_albumart_conditional(const char *wps_bufptr, + struct wps_token *token, + struct wps_data *wps_data) +{ + struct wps_token *prevtoken = token; + --prevtoken; + if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL) + { + /* This %C is part of a %?C construct. + It's either %?C or %?Cn */ + token->type = WPS_TOKEN_ALBUMART_FOUND; + if (*wps_bufptr == 'n' && *(wps_bufptr+1) == '<') + { + token->next = true; + return 1; + } + else if (*wps_bufptr == '<') + { + return 0; + } + else + { + token->type = WPS_NO_TOKEN; + return 0; + } + } + else return 0; +}; +#endif /* HAVE_LCD_BITMAP */ + /* Parse a generic token from the given string. Return the length read */ static int parse_token(const char *wps_bufptr, struct wps_data *wps_data) { @@ -915,6 +1132,9 @@ bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */ #endif memset(data, 0, sizeof(*data)); +#ifdef HAVE_LCD_BITMAP + data->wps_uses_albumart = WPS_ALBUMART_NONE; +#endif wps_data_init(data); #ifdef HAVE_REMOTE_LCD data->remote_wps = rwps; Index: apps/playback.c =================================================================== --- apps/playback.c (révision 15556) +++ apps/playback.c (copie de travail) @@ -66,6 +66,7 @@ #include "icons.h" #include "peakmeter.h" #include "action.h" +#include "albumart.h" #endif #include "lang.h" #include "bookmark.h" @@ -217,6 +218,9 @@ int audio_hid; /* The ID for the track's buffer handle */ int id3_hid; /* The ID for the track's metadata handle */ int codec_hid; /* The ID for the track's codec handle */ +#ifdef HAVE_LCD_BITMAP + int aa_hid; /* The ID for the track's album art handle */ +#endif size_t filesize; /* File total length */ @@ -391,6 +395,15 @@ return false; } +#ifdef HAVE_LCD_BITMAP + if (track->aa_hid >= 0) { + if (bufclose(track->aa_hid)) + track->aa_hid = -1; + else + return false; +#endif + } + track->filesize = 0; track->taginfo_ready = false; track->event_sent = false; @@ -635,6 +648,13 @@ #endif /* HAVE_RECORDING */ +#ifdef HAVE_LCD_BITMAP +int audio_current_aa_hid(void) +{ + return CUR_TI->aa_hid; +} +#endif + struct mp3entry* audio_current_track(void) { const char *filename; @@ -2381,6 +2401,16 @@ else track_id3 = bufgetid3(tracks[track_widx].id3_hid); + +#ifdef HAVE_LCD_BITMAP + if (gui_sync_wps_uses_albumart()) + { + char aa_path[MAX_PATH]; + if (find_albumart(track_id3, aa_path, sizeof(aa_path))) + tracks[track_widx].aa_hid = bufopen(aa_path, 0, TYPE_BITMAP); + } +#endif + track_id3->elapsed = 0; enum data_type type = TYPE_PACKET_AUDIO; @@ -3285,6 +3315,9 @@ tracks[i].audio_hid = -1; tracks[i].id3_hid = -1; tracks[i].codec_hid = -1; +#ifdef HAVE_LCD_BITMAP + tracks[i].aa_hid = -1; +#endif } /* Probably safe to say */ Index: apps/SOURCES =================================================================== --- apps/SOURCES (révision 15556) +++ apps/SOURCES (copie de travail) @@ -50,6 +50,7 @@ gui/gwps-common.c #ifdef HAVE_LCD_BITMAP gui/icon.c +gui/albumart.c #endif gui/list.c gui/option_select.c Index: apps/buffering.c =================================================================== --- apps/buffering.c (révision 15556) +++ apps/buffering.c (copie de travail) @@ -48,6 +48,7 @@ #include "playback.h" #include "pcmbuf.h" #include "buffer.h" +#include "bmp.h" #ifdef SIMULATOR #define ata_disk_is_active() 1 @@ -745,7 +746,7 @@ if (h->next && h->filerem == 0 && (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET || - h->type == TYPE_IMAGE || h->type == TYPE_CODEC || + h->type == TYPE_BITMAP || h->type == TYPE_CODEC || h->type == TYPE_ATOMIC_AUDIO)) { /* metadata handle: we can move all of it */ @@ -814,7 +815,19 @@ } } +#ifdef HAVE_LCD_BITMAP +static int load_bitmap(const int fd) +{ + int rc; + struct bitmap *bmp = (struct bitmap *)&buffer[buf_widx]; + bmp->data = &buffer[buf_widx + sizeof(struct bitmap)]; + int free = (int)MIN(buffer_len - BUF_USED, buffer_len - buf_widx); + rc = read_bmp_fd(fd, bmp, free, FORMAT_ANY|FORMAT_TRANSPARENT); + return rc + (rc > 0 ? sizeof(struct bitmap) : 0); +} +#endif + /* MAIN BUFFERING API CALLS ======================== @@ -858,7 +871,6 @@ } strncpy(h->path, file, MAX_PATH); - h->filesize = size; h->filerem = size - offset; h->offset = offset; h->ridx = buf_widx; @@ -867,7 +879,23 @@ h->available = 0; h->type = type; - if (type == TYPE_CUESHEET || type == TYPE_IMAGE) { +#ifdef HAVE_LCD_BITMAP + if (type == TYPE_BITMAP) { + mutex_lock(&llist_mutex); + size = load_bitmap(fd); + if (size <= 0) + return ERR_FILE_ERROR; + h->filerem = 0; + h->available = size; + h->widx = buf_widx + size; /* safe because the data doesn't wrap */ + buf_widx += size; /* safe too */ + mutex_unlock(&llist_mutex); + } +#endif + + h->filesize = size; + + if (type == TYPE_CUESHEET) { h->fd = fd; /* Immediately start buffering those */ LOGFQUEUE("buffering >| Q_BUFFER_HANDLE"); Index: apps/buffering.h =================================================================== --- apps/buffering.h (révision 15556) +++ apps/buffering.h (copie de travail) @@ -30,7 +30,7 @@ TYPE_ATOMIC_AUDIO, TYPE_ID3, TYPE_CUESHEET, - TYPE_IMAGE, + TYPE_BITMAP, TYPE_BUFFER, TYPE_UNKNOWN, }; Index: firmware/export/audio.h =================================================================== --- firmware/export/audio.h (révision 15556) +++ firmware/export/audio.h (copie de travail) @@ -91,6 +91,9 @@ #endif /* CONFIG_CODEC == SWCODEC */ void audio_ff_rewind(long newtime); void audio_flush_and_reload_tracks(void); +#ifdef HAVE_LCD_BITMAP +int audio_current_aa_hid(void); +#endif struct mp3entry* audio_current_track(void); struct mp3entry* audio_next_track(void); bool audio_has_changed_track(void);