Index: apps/metadata.c =================================================================== RCS file: /cvsroot/rockbox/apps/metadata.c,v retrieving revision 1.42 diff -u -r1.42 metadata.c --- rockbox/apps/metadata.c 11 Apr 2006 03:54:24 -0000 1.42 +++ apps/metadata.c 12 Apr 2006 12:56:23 -0000 @@ -29,6 +29,7 @@ #include "replaygain.h" #include "debug.h" #include "system.h" +#include "gwps.h" enum tagtype { TAGTYPE_APE = 1, TAGTYPE_VORBIS }; @@ -1413,11 +1414,114 @@ return AFMT_UNKNOWN; } +/* Strip filename from a full path + * + * buf - buffer to extract directory to. + * buf_size - size of buffer. + * fullpath - fullpath to extract from. + * + * Returns the full path to the file, minus the file name (including the last slash). + */ +static char* strip_filename(char* buf, int buf_size, const char* fullpath) +{ + char* sep; + int len; + + if (!buf || buf_size <= 0 || !fullpath) + return NULL; + + buf[0] = 0; + + /* if 'fullpath' is not a full path, but only a filename, return immediately */ + sep = strrchr(fullpath,'/'); + if (sep == NULL) + return NULL; + + len = MIN(sep - fullpath + 1, buf_size - 1); + strncpy(buf, fullpath, len); + buf[len] = 0; + return buf; +} + +static char* strip_extension(char* buf, int buf_size, const char* file) +{ + char* sep; + int len; + + if (!buf || buf_size <= 0 || !file) + return NULL; + + buf[0] = 0; + + sep = strrchr(file,'.'); + if (sep == NULL) + return NULL; + + len = MIN(sep - file, buf_size - 1); + strncpy(buf, file, len); + buf[len] = 0; + return buf; +} + +static bool file_exists(char *file) +{ + int fd; + + if (!file || strlen(file) <= 0) + return false; + + fd = open(file, O_RDONLY); + if (fd<0) + return false; + close(fd); + return true; +} + +/* Look for album art in the same dir as the track. + * Returns true if an album art was found, false otherwise */ +static bool find_albumart(struct track_info* track, const char* trackname) +{ + char path[MAX_PATH+1]; + + if (!track || !trackname) + return false; + + strip_extension(path, sizeof(path) - 4, trackname); + strcat(path, ".bmp"); /* the first file we look for is one specific to the track playing */ + if (!file_exists(path)) /* if it doesn't exist, we look for a file specific to the track's album name */ + { + char dir[MAX_PATH+1]; + strip_filename(dir, sizeof(dir), trackname); + if (track->id3.album && strlen(track->id3.album) > 0) + { + snprintf(path, sizeof(path)-1, + "%s%s.bmp", + (strlen(dir) >= 1) ? dir : "", + track->id3.album); + path[sizeof(path)-1] = 0; + } + + if (!file_exists(path)) /* if it still doesn't exist, we look for a generic file */ + { + snprintf(path, sizeof(path)-1, + "%scover.bmp", + (strlen(dir) >= 1) ? dir : ""); + path[sizeof(path)-1] = 0; + if (!file_exists(path)) + return false; + } + } + + strcpy(track->id3.albumart_path, path); + DEBUGF("Album art found for %s : %s\n", trackname, path); + return true; +} + /* Get metadata for track - return false if parsing showed problems with the * file that would prevent playback. */ bool get_metadata(struct track_info* track, int fd, const char* trackname, - bool v1first) + bool v1first, bool search_album_art) { #if CONFIG_CODEC == SWCODEC unsigned char* buf; @@ -1619,10 +1723,14 @@ /* We have successfully read the metadata from the file */ + if (search_album_art && gui_sync_wps_has_albumart()) + { + track->id3.albumart_found = find_albumart(track, trackname); + } + lseek(fd, 0, SEEK_SET); strncpy(track->id3.path, trackname, sizeof(track->id3.path)); track->taginfo_ready = true; return true; } - Index: apps/metadata.h =================================================================== RCS file: /cvsroot/rockbox/apps/metadata.h,v retrieving revision 1.4 diff -u -r1.4 metadata.h --- rockbox/apps/metadata.h 26 Mar 2006 11:33:41 -0000 1.4 +++ apps/metadata.h 12 Apr 2006 12:56:23 -0000 @@ -24,7 +24,7 @@ unsigned int probe_file_format(const char *filename); bool get_metadata(struct track_info* track, int fd, const char* trackname, - bool v1first); + bool v1first, bool search_album_art); #endif Index: apps/playback.c =================================================================== RCS file: /cvsroot/rockbox/apps/playback.c,v retrieving revision 1.262 diff -u -r1.262 playback.c --- rockbox/apps/playback.c 12 Apr 2006 03:25:54 -0000 1.262 +++ apps/playback.c 12 Apr 2006 12:56:23 -0000 @@ -1601,7 +1601,7 @@ if (fd < 0) return false; - status = get_metadata(&tracks[next_idx],fd,trackname,v1first); + status = get_metadata(&tracks[next_idx],fd,trackname,v1first,true); /* Preload the glyphs in the tags */ if (status) { if (tracks[next_idx].id3.title) @@ -1676,7 +1676,7 @@ /* Get track metadata if we don't already have it. */ if (!tracks[track_widx].taginfo_ready) { - if (get_metadata(&tracks[track_widx],current_fd,trackname,v1first)) { + if (get_metadata(&tracks[track_widx],current_fd,trackname,v1first,true)) { if (start_play) { track_changed = true; playlist_update_resume_info(audio_current_track()); @@ -2951,4 +2951,3 @@ queue_post(&audio_queue, Q_AUDIO_POSTINIT, 0); } - Index: apps/tagcache.c =================================================================== RCS file: /cvsroot/rockbox/apps/tagcache.c,v retrieving revision 1.17 diff -u -r1.17 tagcache.c --- rockbox/apps/tagcache.c 12 Apr 2006 10:31:24 -0000 1.17 +++ apps/tagcache.c 12 Apr 2006 12:56:24 -0000 @@ -1170,7 +1170,7 @@ memset(&track, 0, sizeof(struct track_info)); memset(&entry, 0, sizeof(struct temp_file_entry)); memset(&tracknumfix, 0, sizeof(tracknumfix)); - ret = get_metadata(&track, fd, path, false); + ret = get_metadata(&track, fd, path, false, false); close(fd); if (!ret) Index: apps/gui/gwps-common.c =================================================================== RCS file: /cvsroot/rockbox/apps/gui/gwps-common.c,v retrieving revision 1.50 diff -u -r1.50 gwps-common.c --- rockbox/apps/gui/gwps-common.c 6 Apr 2006 04:07:06 -0000 1.50 +++ apps/gui/gwps-common.c 12 Apr 2006 12:56:25 -0000 @@ -63,6 +63,10 @@ /* 3% of 30min file == 54s step size */ #define MIN_FF_REWIND_STEP 500 +static char cur_displayed_albumart_path[MAX_PATH] = { "" }; +static int cur_displayed_albumart_width = -1; +static int cur_displayed_albumart_height = -1; + /* Skip leading UTF-8 BOM, if present. */ static char* skip_utf8_bom(char* buf) { @@ -99,6 +103,7 @@ * %xl - preload image * %we - enable statusbar on wps regardless of the global setting * %wd - disable statusbar on wps regardless of the global setting + * %C - display a cover bitmap at given position * and also for: * # - a comment line * @@ -176,6 +181,83 @@ break; #endif + case 'C': + /* Album cover */ + /* format: %C|x|y|[maxwidth|maxheight|] */ + /* format: %C|x|y|[[+|-]maxwidth|[+|-]maxheight|] */ + { + char *pos = buf+1; + + /* get x-position */ + pos = strchr(pos, '|'); + if (!pos) + return false; + data->albumart_x = atoi(++pos); + + /* get y-position */ + pos = strchr(pos, '|'); + if (!pos) + return false; + data->albumart_y = atoi(++pos); + + /* default max width and height are set to -1 (don't use it) */ + data->albumart_max_width = -1; + data->albumart_max_height = -1; + data->albumart_xalign = -1; + data->albumart_yalign = -1; + + /* the next fields are optional... */ + /* get max width */ + pos = strchr(pos, '|'); + if (pos) + { + ++pos; + data->albumart_xalign = WPS_ALIGN_CENTER; + if (*pos == '-' || *pos == '+') + { + if (*pos == '-') + data->albumart_xalign = WPS_ALIGN_RIGHT; + else + data->albumart_xalign = WPS_ALIGN_LEFT; + ++pos; + } + + data->albumart_max_width = atoi(pos); + if (data->albumart_max_width > LCD_WIDTH) + data->albumart_max_width = LCD_WIDTH; + else + if (data->albumart_max_width <= 0) + data->albumart_max_width = -1; + + pos = strchr(pos, '|'); + if (pos) + { + ++pos; + data->albumart_yalign = WPS_ALIGN_CENTER; + if (*pos == '-' || *pos == '+') + { + if (*pos == '-') + data->albumart_yalign = WPS_ALIGN_RIGHT; + else + data->albumart_yalign = WPS_ALIGN_LEFT; + ++pos; + } + + data->albumart_max_height = atoi(pos); + if (data->albumart_max_height > LCD_HEIGHT) + data->albumart_max_height = LCD_HEIGHT; + else + if (data->albumart_max_height <= 0) + data->albumart_max_height = -1; + } + } + + data->wps_has_albumart = true; + return true; + } + + break; + case 'P': /* progress bar image */ { @@ -433,7 +515,7 @@ /* Get the tag specified by the two characters at fmt. * - * cid3 - ID3 data to get tag values from. + * cid3 - ID3 data to get tag values from. * nid3 - next-song ID3 data to get tag values from. * tag - string (of two characters) specifying the tag to get. * buf - buffer to certain tags, such as track number, play time or @@ -1070,6 +1152,18 @@ return buf; } #endif /* CONFIG_RTC */ + + case 'C': /* Album cover */ + { + *flags |= WPS_REFRESH_DYNAMIC; + if (tag[1] == 'n') /* next track */ + id3 = nid3; + + if (id3 && id3->albumart_found) + return "C"; + else + return NULL; + } break; } return NULL; } @@ -1570,6 +1664,70 @@ wps_draw_image(gwps, n); } } + + /* display album art only if the wps has a tag for it, */ + /* AND if it's smaller than the max size given in the WPS tags (if given). */ + /* There is no need to check the bitmap is smaller than the screen, */ + /* because if it's bigger, it won't have been loaded */ + if (data->wps_has_albumart + && gwps->state->id3->albumart_data != NULL + && (data->albumart_max_width < 0 + || gwps->state->id3->albumart_width <= (unsigned)data->albumart_max_width) + && (data->albumart_max_height < 0 + || gwps->state->id3->albumart_height <= (unsigned)data->albumart_max_height)) + { + /* coordinates where the cover will be drawn */ + int x = data->albumart_x; + int y = data->albumart_y; + + /* if a bounding box was defined, + ** position the cover art either centered, left or right + ** to the margins of the box + ** otherwise, just draw at the normal coordinate + */ + if (data->albumart_max_width > 0) + { + switch (data->albumart_xalign) + { + case WPS_ALIGN_LEFT: + ; + break; + + case WPS_ALIGN_RIGHT: + x += data->albumart_max_width - gwps->state->id3->albumart_width; + break; + + default: /* WPS_ALIGN_CENTER */ + x += (data->albumart_max_width - gwps->state->id3->albumart_width)/2; + break; + } + } + if (data->albumart_max_height > 0) + { + switch (data->albumart_yalign) + { + case WPS_ALIGN_LEFT: + ; + break; + + case WPS_ALIGN_RIGHT: + y += data->albumart_max_height - gwps->state->id3->albumart_height; + break; + + default: /* WPS_ALIGN_CENTER */ + y += (data->albumart_max_height - gwps->state->id3->albumart_height)/2; + break; + } + } + + /* display cover */ + gwps->display->set_drawmode(DRMODE_FG); + gwps->display->transparent_bitmap( + (fb_data *)gwps->state->id3->albumart_data, + x, y, gwps->state->id3->albumart_width, + gwps->state->id3->albumart_height); + } + display->set_drawmode(DRMODE_SOLID); } #endif @@ -2501,11 +2659,54 @@ { gwps->display->stop_scroll(); gwps->state->id3 = audio_current_track(); + + /* display album cover bitmap (if present and needed) */ + if (gui_wps->data->wps_has_albumart + && gwps->state->id3->albumart_found) + { + if ((strcasecmp(cur_displayed_albumart_path, + gwps->state->id3->albumart_path) != 0) || + (cur_displayed_albumart_width != gui_wps->data->albumart_max_width) || + (cur_displayed_albumart_height != gui_wps->data->albumart_max_height)) + { + /* load the cover art bitmap */ + int rc; + struct bitmap temp_bm; + temp_bm.data = gui_wps->data->img_buf_ptr; + rc = read_bmp_file(gwps->state->id3->albumart_path, + &temp_bm, gui_wps->data->img_buf_free, + FORMAT_NATIVE|FORMAT_TRANSPARENT, + gui_wps->data->albumart_max_width, gui_wps->data->albumart_max_height); + if (rc > 0) + { + //gwps->state->id3->albumart_size = cur_displayed_albumart_size = rc; + gwps->state->id3->albumart_width = cur_displayed_albumart_width = temp_bm.width; + gwps->state->id3->albumart_height = cur_displayed_albumart_height = temp_bm.height; + gwps->state->id3->albumart_data = (fb_data*)gui_wps->data->img_buf_ptr; + strcpy(cur_displayed_albumart_path, gwps->state->id3->albumart_path); + DEBUGF("** Loaded album art for %s : %d bytes\n", gwps->state->id3->path, rc); + } + else + { + gwps->state->id3->albumart_data = NULL; + strcpy(cur_displayed_albumart_path, ""); + DEBUGF("Loading album art for %s failed\n", gwps->state->id3->path); + } + } + else + { + gwps->state->id3->albumart_data = (fb_data*)gui_wps->data->img_buf_ptr; + gwps->state->id3->albumart_width = cur_displayed_albumart_width; + gwps->state->id3->albumart_height = cur_displayed_albumart_height; + //gwps->state->id3->albumart_size = cur_displayed_albumart_size; + DEBUGF("Skipped loading album art\n"); + } + } + if (gui_wps_display()) retcode = true; - else{ + else gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL); - } if (gwps->state->id3) memcpy(gwps->state->current_track_path, gwps->state->id3->path, Index: apps/gui/gwps.c =================================================================== RCS file: /cvsroot/rockbox/apps/gui/gwps.c,v retrieving revision 1.38 diff -u -r1.38 gwps.c --- rockbox/apps/gui/gwps.c 11 Apr 2006 01:41:40 -0000 1.38 +++ apps/gui/gwps.c 12 Apr 2006 12:56:25 -0000 @@ -879,6 +879,7 @@ static void wps_reset(struct wps_data *data) { data->wps_loaded = false; + data->wps_has_albumart = false; memset(&data->format_buffer, 0, sizeof data->format_buffer); wps_data_init(data); } @@ -1063,3 +1064,20 @@ unload_wps_backdrop(); #endif } + +/* +** returns true if at least one of the gui_wps screens +** have albumart in their wps structure +*/ +bool gui_sync_wps_has_albumart(void) +{ + int i; + FOR_NB_SCREENS(i) { + struct gui_wps *gwps = &gui_wps[i]; + if (gwps->data && gwps->data->wps_has_albumart) + return true; + } + return false; +} + +/* vi: set ts=4 sts=4 sw=4 et ai: */ Index: apps/gui/gwps.h =================================================================== RCS file: /cvsroot/rockbox/apps/gui/gwps.h,v retrieving revision 1.32 diff -u -r1.32 gwps.h --- rockbox/apps/gui/gwps.h 10 Apr 2006 03:51:17 -0000 1.32 +++ apps/gui/gwps.h 12 Apr 2006 12:56:26 -0000 @@ -350,6 +350,15 @@ int img_buf_free; bool wps_sb_tag; bool show_sb_on_wps; + + /* album art support */ + bool wps_has_albumart; + int albumart_x; + int albumart_y; + short albumart_xalign; /* WPS_ALIGN_CENTER, WPS_ALIGN_LEFT, WPS_ALIGN_RIGHT */ + short albumart_yalign; /* WPS_ALIGN_CENTER, WPS_ALIGN_LEFT, WPS_ALIGN_RIGHT */ + int albumart_max_width; + int albumart_max_height; #endif #ifdef HAVE_LCD_CHARCELLS unsigned char wps_progress_pat[8]; @@ -441,4 +450,8 @@ void gui_sync_wps_init(void); void gui_sync_wps_screen_init(void); +/* gives back if albumart should be loaded or not */ +bool gui_sync_wps_has_albumart(void); + + #endif Index: firmware/export/id3.h =================================================================== RCS file: /cvsroot/rockbox/firmware/export/id3.h,v retrieving revision 1.26 diff -u -r1.26 id3.h --- rockbox/firmware/export/id3.h 1 Feb 2006 16:42:02 -0000 1.26 +++ rockbox/firmware/export/id3.h 12 Apr 2006 12:56:26 -0000 @@ -22,6 +22,8 @@ #include #include "config.h" #include "file.h" +#include "lcd.h" +#include "system.h" /* Audio file types. */ /* NOTE: When adding new audio types, also add to codec_labels[] in id3.c */ @@ -125,6 +127,15 @@ long track_peak; /* 7.24 signed fixed point. 0 for no peak. */ long album_peak; #endif + +#ifdef HAVE_LCD_BITMAP + /* album art support */ + fb_data* albumart_data; + unsigned int albumart_width; + unsigned int albumart_height; + bool albumart_found; + char albumart_path[MAX_PATH]; +#endif }; enum {