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 recorder/icons.c recorder/keyboard.c recorder/peakmeter.c +#ifdef HAVE_ALBUMART +recorder/albumart.c +#endif #ifdef HAVE_LCD_COLOR gui/color_picker.c #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 @@ #include "playback.h" #include "pcmbuf.h" #include "buffer.h" +#include "bmp.h" #ifdef SIMULATOR #define ata_disk_is_active() 1 @@ -745,7 +746,7 @@ static void shrink_handle(struct memory_handle *h) 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 */ @@ -762,11 +763,15 @@ static void shrink_handle(struct memory_handle *h) h->ridx = RINGBUF_ADD(h->ridx, delta); h->widx = RINGBUF_ADD(h->widx, delta); - /* when moving a struct mp3entry we need to readjust its pointers. */ if (h->type == TYPE_ID3 && h->filesize == sizeof(struct mp3entry)) { + /* when moving an mp3entry we need to readjust its pointers. */ adjust_mp3entry((struct mp3entry *)&buffer[h->data], (void *)&buffer[h->data], (void *)&buffer[olddata]); + } else if (h->type == TYPE_BITMAP) { + /* adjust the bitmap's pointer */ + struct bitmap *bmp = (struct bitmap *)&buffer[h->data]; + bmp->data = &buffer[h->data + sizeof(struct bitmap)]; } } else @@ -814,6 +819,23 @@ static bool fill_buffer(void) } } +#ifdef HAVE_LCD_BITMAP +/* Given a file descriptor to a bitmap file, write the bitmap data to the + buffer, with a struct bitmap and the actual data immediately following. + Return value is the total size (struct + data). */ +static int load_bitmap(const int fd) +{ + int rc; + struct bitmap *bmp = (struct bitmap *)&buffer[buf_widx]; + /* FIXME: alignment may be needed for the data buffer. */ + bmp->data = &buffer[buf_widx + sizeof(struct bitmap)]; + bmp->maskdata = NULL; + int free = (int)MIN(buffer_len - BUF_USED, buffer_len - buf_widx); + rc = read_bmp_fd(fd, bmp, free, FORMAT_ANY|FORMAT_DITHER); + return rc + (rc > 0 ? sizeof(struct bitmap) : 0); +} +#endif + /* MAIN BUFFERING API CALLS @@ -858,7 +880,6 @@ int bufopen(const char *file, size_t offset, enum data_type type) } strncpy(h->path, file, MAX_PATH); - h->filesize = size; h->filerem = size - offset; h->offset = offset; h->ridx = buf_widx; @@ -867,7 +888,25 @@ int bufopen(const char *file, size_t offset, enum data_type type) h->available = 0; h->type = type; - if (type == TYPE_CUESHEET || type == TYPE_IMAGE) { +#ifdef HAVE_LCD_BITMAP + if (type == TYPE_BITMAP) { + /* Bitmap file: we load the data instead of the file */ + mutex_lock(&llist_mutex); /* Lock because load_bitmap yields */ + 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"); 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 { TYPE_ATOMIC_AUDIO, TYPE_ID3, TYPE_CUESHEET, - TYPE_IMAGE, + TYPE_BITMAP, TYPE_BUFFER, TYPE_UNKNOWN, }; 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 @@ /* Image stuff */ #include "bmp.h" #include "atoi.h" +#include "albumart.h" #endif #include "dsp.h" #include "action.h" @@ -929,6 +930,19 @@ static char *get_token_value(struct gui_wps *gwps, case WPS_TOKEN_METADATA_COMMENT: return id3->comment; +#ifdef HAVE_ALBUMART + 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); 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) unload_remote_wps_backdrop(); #endif } + +#ifdef HAVE_ALBUMART +/* Returns true if at least one of the gui_wps screens has an album art + 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; +} +#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 @@ #define WPS_ALIGN_CENTER 64 #define WPS_ALIGN_LEFT 128 +#ifdef HAVE_ALBUMART + +/* 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 */ + +#endif /* HAVE_ALBUMART */ + /* wps_data*/ #ifdef HAVE_LCD_BITMAP @@ -187,6 +204,12 @@ enum wps_token_type { WPS_TOKEN_IMAGE_DISPLAY, #endif +#ifdef HAVE_ALBUMART + /* Albumart */ + WPS_TOKEN_ALBUMART_DISPLAY, + WPS_TOKEN_ALBUMART_FOUND, +#endif + /* Metadata */ WPS_TOKEN_METADATA_ARTIST, WPS_TOKEN_METADATA_COMPOSER, @@ -309,6 +332,20 @@ struct wps_data short progress_start; short progress_end; bool peak_meter_enabled; + +#ifdef HAVE_ALBUMART + /* Album art support */ + 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; +#endif + #else /*HAVE_LCD_CHARCELLS */ unsigned short wps_progress_pat[8]; bool full_line_progressbar; @@ -417,4 +454,9 @@ extern struct gui_wps gui_wps[NB_SCREENS]; void gui_sync_wps_init(void); void gui_sync_wps_screen_init(void); +#ifdef HAVE_ALBUMART +/* gives back if WPS contains an albumart tag */ +bool gui_sync_wps_uses_albumart(void); +#endif + #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, struct wps_token *token, struct wps_data *wps_data); static int parse_dir_level(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); + #ifdef HAVE_LCD_BITMAP static int parse_image_special(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); @@ -126,6 +127,13 @@ static int parse_image_load(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); #endif /*HAVE_LCD_BITMAP */ +#ifdef HAVE_ALBUMART +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_ALBUMART */ + #ifdef CONFIG_RTC #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC #else @@ -283,6 +291,11 @@ static const struct wps_tag all_tags[] = { { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load }, { WPS_TOKEN_IMAGE_PROGRESS_BAR, "P", 0, parse_image_special }, +#ifdef HAVE_ALBUMART + { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load }, + { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_DYNAMIC, + parse_albumart_conditional }, +#endif #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1)) { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special }, #endif @@ -606,6 +619,215 @@ static int parse_progressbar(const char *wps_bufptr, #endif } +#ifdef HAVE_ALBUMART +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 '-': + 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 '+': + 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_ALBUMART */ + /* 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 +1137,9 @@ static void wps_reset(struct wps_data *data) bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */ #endif memset(data, 0, sizeof(*data)); +#ifdef HAVE_ALBUMART + data->wps_uses_albumart = WPS_ALBUMART_NONE; +#endif wps_data_init(data); #ifdef HAVE_REMOTE_LCD 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 @@ #include "icons.h" #include "peakmeter.h" #include "action.h" +#include "albumart.h" #endif #include "lang.h" #include "bookmark.h" @@ -217,6 +218,9 @@ struct track_info { 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_ALBUMART + int aa_hid; /* The ID for the track's album art handle */ +#endif size_t filesize; /* File total length */ @@ -391,6 +395,15 @@ static bool clear_track_info(struct track_info *track) return false; } +#ifdef HAVE_ALBUMART + 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 @@ void audio_remove_encoder(void) #endif /* HAVE_RECORDING */ +#ifdef HAVE_ALBUMART +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 @@ static bool audio_load_track(int offset, bool start_play) else track_id3 = bufgetid3(tracks[track_widx].id3_hid); + +#ifdef HAVE_ALBUMART + 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; @@ -3286,6 +3316,9 @@ void audio_init(void) tracks[i].audio_hid = -1; tracks[i].id3_hid = -1; tracks[i].codec_hid = -1; +#ifdef HAVE_ALBUMART + tracks[i].aa_hid = -1; +#endif } /* 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Nicolas Pennequin + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include "sprintf.h" +#include "system.h" +#include "albumart.h" +#include "id3.h" +#include "gwps.h" +#include "buffering.h" +#include "dircache.h" +#include "debug.h" + + +/* Strip filename from a full path + * + * buf - buffer to extract directory to. + * buf_size - size of buffer. + * fullpath - fullpath to extract from. + * + * Split the directory part of the given fullpath and store it in buf + * (including last '/'). + * The function return parameter is a pointer to the filename + * inside the given fullpath. + */ +static char* strip_filename(char* buf, int buf_size, const char* fullpath) +{ + char* sep; + int len; + + if (!buf || buf_size <= 0 || !fullpath) + return NULL; + + /* if 'fullpath' is only a filename return immediately */ + sep = strrchr(fullpath, '/'); + if (sep == NULL) + { + buf[0] = 0; + return (char*)fullpath; + } + + len = MIN(sep - fullpath + 1, buf_size - 1); + strncpy(buf, fullpath, len); + buf[len] = 0; + return (sep + 1); +} + +/* Strip extension from a filename. + * + * buf - buffer to output the result to. + * buf_size - size of the output buffer buffer. + * file - filename to strip extension from. + * + * Return value is a pointer to buf, which contains the result. + */ +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; +} + +/* Test file existence, using dircache of possible */ +static bool file_exists(const char *file) +{ + int fd; + + if (!file || strlen(file) <= 0) + return false; + +#ifdef HAVE_DIRCACHE + if (dircache_is_enabled()) + return (dircache_get_entry_ptr(file) != NULL); +#endif + + fd = open(file, O_RDONLY); + if (fd < 0) + return false; + close(fd); + return true; +} + +/* Look for the first matching album art bitmap in the following list: + * ./.bmp + * ./.bmp + * ./cover.bmp + * ../.bmp + * ../cover.bmp + * is the value of the size_string parameter, and + * are read from the ID3 metadata. + * If a matching bitmap is found, its filename is stored in buf. + * Return value is true if a bitmap was found, false otherwise. + */ +static bool search_files(const struct mp3entry *id3, const char *size_string, + char *buf, int buflen) +{ + char path[MAX_PATH + 1]; + char dir[MAX_PATH + 1]; + bool found = false; + const char *trackname; + + if (!id3 || !buf) + return false; + + trackname = id3->path; + strip_filename(dir, sizeof(dir), trackname); + + /* the first file we look for is one specific to the track playing */ + strip_extension(path, sizeof(path) - strlen(size_string) - 4, trackname); + strcat(path, size_string); + strcat(path, ".bmp"); + found = file_exists(path); + if (!found && id3->album && strlen(id3->album) > 0) + { + /* if it doesn't exist, + * we look for a file specific to the track's album name */ + snprintf(path, sizeof(path) - 1, + "%s%s%s.bmp", + (strlen(dir) >= 1) ? dir : "", + id3->album, size_string); + path[sizeof(path) - 1] = 0; + found = file_exists(path); + } + + if (!found) + { + /* if it still doesn't exist, we look for a generic file */ + snprintf(path, sizeof(path)-1, + "%scover%s.bmp", + (strlen(dir) >= 1) ? dir : "", size_string); + path[sizeof(path)-1] = 0; + found = file_exists(path); + } + + if (!found) + { + /* if it still doesn't exist, + * we continue to search in the parent directory */ + char temp[MAX_PATH + 1]; + strncpy(temp, dir, strlen(dir) - 1); + temp[strlen(dir) - 1] = 0; + + strip_filename(dir, sizeof(dir), temp); + } + + if (!found && id3->album && strlen(id3->album) > 0) + { + /* we look in the parent directory + * for a file specific to the track's album name */ + snprintf(path, sizeof(path)-1, + "%s%s%s.bmp", + (strlen(dir) >= 1) ? dir : "", + id3->album, size_string); + found = file_exists(path); + } + + if (!found) + { + /* if it still doesn't exist, we look in the parent directory + * for a generic file */ + snprintf(path, sizeof(path)-1, + "%scover%s.bmp", + (strlen(dir) >= 1) ? dir : "", size_string); + path[sizeof(path)-1] = 0; + found = file_exists(path); + } + + if (!found) + return false; + + strncpy(buf, path, buflen); + DEBUGF("Album art found: %s\n", path); + return true; +} + +/* Look for albumart bitmap in the same dir as the track and in its parent dir. + * Stores the found filename in the buf parameter. + * Returns true if a bitmap was found, false otherwise */ +bool find_albumart(const struct mp3entry *id3, char *buf, int buflen) +{ + if (!id3 || !buf) + return false; + + char size_string[9]; + struct wps_data *data = gui_wps[0].data; + + if (!data) + return false; + + DEBUGF("Looking for album art for %s\n", id3->path); + + /* Write the size string, e.g. ".100x100". */ + snprintf(size_string, sizeof(size_string), ".%dx%d", + data->albumart_max_width, data->albumart_max_height); + + /* First we look for a bitmap of the right size */ + if (search_files(id3, size_string, buf, buflen)) + return true; + + /* Then we look for generic bitmaps */ + *size_string = 0; + return search_files(id3, size_string, buf, buflen); +} + +/* Draw the album art bitmap from the given handle ID onto the given WPS. */ +void draw_album_art(struct gui_wps *gwps, int handle_id) +{ + if (!gwps || !gwps->data || !gwps->display || handle_id < 0) + return; + + struct wps_data *data = gwps->data; + +#ifdef HAVE_REMOTE_LCD + /* No album art on RWPS */ + if (data->remote_wps) + return; +#endif + + struct bitmap *bmp; + bufgetdata(handle_id, 0, (void *)&bmp); + + short x = data->albumart_x; + short y = data->albumart_y; + short width = bmp->width; + short height = bmp->height; + + if (data->albumart_max_width > 0) + { + /* Crop if the bitmap is too wide */ + width = MIN(bmp->width, data->albumart_max_width); + + /* Align */ + if (data->albumart_xalign & WPS_ALBUMART_ALIGN_RIGHT) + x += data->albumart_max_width - width; + else if (data->albumart_xalign & WPS_ALBUMART_ALIGN_CENTER) + x += (data->albumart_max_width - width) / 2; + } + + if (data->albumart_max_height > 0) + { + /* Crop if the bitmap is too high */ + height = MIN(bmp->height, data->albumart_max_height); + + /* Align */ + if (data->albumart_yalign & WPS_ALBUMART_ALIGN_BOTTOM) + y += data->albumart_max_height - height; + else if (data->albumart_yalign & WPS_ALBUMART_ALIGN_CENTER) + y += (data->albumart_max_height - height) / 2; + } + + /* Draw the bitmap */ + gwps->display->set_drawmode(DRMODE_FG); + gwps->display->bitmap_part((fb_data*)bmp->data, 0, 0, bmp->width, + x, y, width, height); + gwps->display->set_drawmode(DRMODE_SOLID); +} 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Nicolas Pennequin + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef _ALBUMART_H_ +#define _ALBUMART_H_ + +#ifdef HAVE_ALBUMART + +#include +#include "id3.h" +#include "gwps.h" + +/* Look for albumart bitmap in the same dir as the track and in its parent dir. + * Stores the found filename in the buf parameter. + * Returns true if a bitmap was found, false otherwise */ +bool find_albumart(const struct mp3entry *id3, char *buf, int buflen); + +/* Draw the album art bitmap from the given handle ID onto the given WPS. */ +void draw_album_art(struct gui_wps *gwps, int handle_id); + +#endif /* HAVE_ALBUMART */ + +#endif /* _ALBUMART_H_ */ diff --git a/firmware/export/audio.h b/firmware/export/audio.h index 84275cca2a..468856a87a 100644 --- a/firmware/export/audio.h +++ b/firmware/export/audio.h @@ -91,6 +91,9 @@ void audio_pre_ff_rewind(void); /* SWCODEC only */ #endif /* CONFIG_CODEC == SWCODEC */ void audio_ff_rewind(long newtime); void audio_flush_and_reload_tracks(void); +#ifdef HAVE_ALBUMART +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); diff --git a/firmware/export/config-c200.h b/firmware/export/config-c200.h index 2e3423c79f..8a1d6a37fa 100644 --- a/firmware/export/config-c200.h +++ b/firmware/export/config-c200.h @@ -26,6 +26,9 @@ /* define this if you have a colour LCD */ #define HAVE_LCD_COLOR +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you have a light associated with the buttons */ #define HAVE_BUTTON_LIGHT diff --git a/firmware/export/config-e200.h b/firmware/export/config-e200.h index 5e9103b002..e2274bc956 100644 --- a/firmware/export/config-e200.h +++ b/firmware/export/config-e200.h @@ -26,6 +26,9 @@ /* define this if you have a colour LCD */ #define HAVE_LCD_COLOR +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you have a light associated with the buttons */ #define HAVE_BUTTON_LIGHT diff --git a/firmware/export/config-gigabeat.h b/firmware/export/config-gigabeat.h index ba6b714a17..2e3ea07b36 100644 --- a/firmware/export/config-gigabeat.h +++ b/firmware/export/config-gigabeat.h @@ -14,6 +14,9 @@ /* define this if you have a colour LCD */ #define HAVE_LCD_COLOR +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you have access to the quickscreen */ #define HAVE_QUICKSCREEN diff --git a/firmware/export/config-h10.h b/firmware/export/config-h10.h index 8054152c77..039ebc588f 100644 --- a/firmware/export/config-h10.h +++ b/firmware/export/config-h10.h @@ -26,6 +26,9 @@ /* define this if you have a colour LCD */ #define HAVE_LCD_COLOR +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you have access to the quickscreen */ #define HAVE_QUICKSCREEN diff --git a/firmware/export/config-h100.h b/firmware/export/config-h100.h index c661e1df14..f6f53f64d0 100644 --- a/firmware/export/config-h100.h +++ b/firmware/export/config-h100.h @@ -15,6 +15,9 @@ /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you can flip your LCD */ #define HAVE_LCD_FLIP diff --git a/firmware/export/config-h10_5gb.h b/firmware/export/config-h10_5gb.h index cb5d4ecae0..3d10af26cf 100644 --- a/firmware/export/config-h10_5gb.h +++ b/firmware/export/config-h10_5gb.h @@ -26,6 +26,9 @@ /* define this if you have a colour LCD */ #define HAVE_LCD_COLOR +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you have access to the quickscreen */ #define HAVE_QUICKSCREEN diff --git a/firmware/export/config-h120.h b/firmware/export/config-h120.h index 6fc9aa2406..c347cd6ae0 100644 --- a/firmware/export/config-h120.h +++ b/firmware/export/config-h120.h @@ -10,6 +10,9 @@ /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you can flip your LCD */ #define HAVE_LCD_FLIP diff --git a/firmware/export/config-h300.h b/firmware/export/config-h300.h index 483582080e..de478aa163 100644 --- a/firmware/export/config-h300.h +++ b/firmware/export/config-h300.h @@ -13,6 +13,9 @@ /* define this if you have a colour LCD */ #define HAVE_LCD_COLOR +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you can flip your LCD */ #define HAVE_LCD_FLIP diff --git a/firmware/export/config-iaudiom5.h b/firmware/export/config-iaudiom5.h index 3e98f4e748..9330315305 100644 --- a/firmware/export/config-iaudiom5.h +++ b/firmware/export/config-iaudiom5.h @@ -22,6 +22,9 @@ /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you can flip your LCD */ #define HAVE_LCD_FLIP diff --git a/firmware/export/config-iaudiox5.h b/firmware/export/config-iaudiox5.h index c78137c721..d6fe68b1ac 100644 --- a/firmware/export/config-iaudiox5.h +++ b/firmware/export/config-iaudiox5.h @@ -28,6 +28,9 @@ /* define this if you have a colour LCD */ #define HAVE_LCD_COLOR +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you can invert the colours on your LCD */ #define HAVE_LCD_INVERT diff --git a/firmware/export/config-ipod1g2g.h b/firmware/export/config-ipod1g2g.h index 138ef8dc77..c003805986 100644 --- a/firmware/export/config-ipod1g2g.h +++ b/firmware/export/config-ipod1g2g.h @@ -14,6 +14,9 @@ /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you can invert the colours on your LCD */ #define HAVE_LCD_INVERT diff --git a/firmware/export/config-ipod3g.h b/firmware/export/config-ipod3g.h index e3c2a922d9..c3c6114d16 100644 --- a/firmware/export/config-ipod3g.h +++ b/firmware/export/config-ipod3g.h @@ -14,6 +14,9 @@ /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you can invert the colours on your LCD */ #define HAVE_LCD_INVERT diff --git a/firmware/export/config-ipod4g.h b/firmware/export/config-ipod4g.h index 7d58992ee8..8bdc053563 100644 --- a/firmware/export/config-ipod4g.h +++ b/firmware/export/config-ipod4g.h @@ -24,6 +24,9 @@ /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you can invert the colours on your LCD */ #define HAVE_LCD_INVERT diff --git a/firmware/export/config-ipodcolor.h b/firmware/export/config-ipodcolor.h index a5850904fa..0aba8c34d6 100644 --- a/firmware/export/config-ipodcolor.h +++ b/firmware/export/config-ipodcolor.h @@ -27,6 +27,9 @@ /* define this if you have a colour LCD */ #define HAVE_LCD_COLOR +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you have access to the quickscreen */ #define HAVE_QUICKSCREEN /* define this if you have access to the pitchscreen */ diff --git a/firmware/export/config-ipodmini.h b/firmware/export/config-ipodmini.h index 5cab1767e3..4a3edbfb5f 100644 --- a/firmware/export/config-ipodmini.h +++ b/firmware/export/config-ipodmini.h @@ -14,6 +14,9 @@ /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you can invert the colours on your LCD */ #define HAVE_LCD_INVERT diff --git a/firmware/export/config-ipodmini2g.h b/firmware/export/config-ipodmini2g.h index 77299dda01..0f82d099b9 100644 --- a/firmware/export/config-ipodmini2g.h +++ b/firmware/export/config-ipodmini2g.h @@ -14,6 +14,9 @@ /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you can invert the colours on your LCD */ #define HAVE_LCD_INVERT diff --git a/firmware/export/config-ipodnano.h b/firmware/export/config-ipodnano.h index fcba445bce..4fcb8f69dc 100644 --- a/firmware/export/config-ipodnano.h +++ b/firmware/export/config-ipodnano.h @@ -27,6 +27,9 @@ /* define this if you have a colour LCD */ #define HAVE_LCD_COLOR +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you have access to the quickscreen */ #define HAVE_QUICKSCREEN /* define this if you have access to the pitchscreen */ diff --git a/firmware/export/config-ipodvideo.h b/firmware/export/config-ipodvideo.h index 9ba479246b..eecb65c809 100644 --- a/firmware/export/config-ipodvideo.h +++ b/firmware/export/config-ipodvideo.h @@ -27,6 +27,9 @@ /* define this if you have a colour LCD */ #define HAVE_LCD_COLOR +/* define this if you want album art for this target */ +#define HAVE_ALBUMART + /* define this if you have access to the quickscreen */ #define HAVE_QUICKSCREEN /* define this if you have access to the pitchscreen */