summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--firmware/export/audio.h3
-rw-r--r--firmware/export/config-c200.h3
-rw-r--r--firmware/export/config-e200.h3
-rw-r--r--firmware/export/config-gigabeat.h3
-rw-r--r--firmware/export/config-h10.h3
-rw-r--r--firmware/export/config-h100.h3
-rw-r--r--firmware/export/config-h10_5gb.h3
-rw-r--r--firmware/export/config-h120.h3
-rw-r--r--firmware/export/config-h300.h3
-rw-r--r--firmware/export/config-iaudiom5.h3
-rw-r--r--firmware/export/config-iaudiox5.h3
-rw-r--r--firmware/export/config-ipod1g2g.h3
-rw-r--r--firmware/export/config-ipod3g.h3
-rw-r--r--firmware/export/config-ipod4g.h3
-rw-r--r--firmware/export/config-ipodcolor.h3
-rw-r--r--firmware/export/config-ipodmini.h3
-rw-r--r--firmware/export/config-ipodmini2g.h3
-rw-r--r--firmware/export/config-ipodnano.h3
-rw-r--r--firmware/export/config-ipodvideo.h3
29 files changed, 757 insertions, 5 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index d50da97..5e09704 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 edfb8e7..3a41268 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 1d69df2..06895e7 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 fec13d5..3c29884 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 b7707cd..ad9fce7 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 83ff14b..391fc72 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 8471bff..097a60c 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<blah> or %?Cn<blah> */
+ 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 0b2c9bb..5c526f9 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 0000000..abae8c1
--- /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 <string.h>
+#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:
+ * ./<trackname><size>.bmp
+ * ./<albumname><size>.bmp
+ * ./cover<size>.bmp
+ * ../<albumname><size>.bmp
+ * ../cover<size>.bmp
+ * <size> is the value of the size_string parameter, <trackname> and
+ * <albumname> 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 0000000..21ae50e
--- /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 <stdbool.h>
+#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 84275cc..468856a 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 2e3423c..8a1d6a3 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 5e9103b..e2274bc 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 ba6b714..2e3ea07 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 8054152..039ebc5 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 c661e1d..f6f53f6 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 cb5d4ec..3d10af2 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 6fc9aa2..c347cd6 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 4835820..de478aa 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 3e98f4e..9330315 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 c78137c..d6fe68b 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 138ef8d..c003805 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 e3c2a92..c3c6114 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 7d58992..8bdc053 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 a585090..0aba8c3 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 5cab176..4a3edbf 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 77299dd..0f82d09 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 fcba445..4fcb8f6 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 9ba4792..eecb65c 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 */