Index: tools/genlang =================================================================== --- tools/genlang (revision 14627) +++ tools/genlang (working copy) @@ -529,6 +529,18 @@ /* this contains the concatenation of all strings, separated by \\0 chars */ extern const unsigned char language_builtin[]; +#include "${prefix}_enum.h" + +MOO + ; + + close(HFILE); + + open(HFILE, ">${prefix}_enum.h") || + die "couldn't create file ${prefix}_enum.h\n"; + + print HFILE < 1 + bool remote = format & FORMAT_REMOTE; + format &= ~FORMAT_REMOTE; +#endif + +#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) + if (format == FORMAT_NATIVE) { +# if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 + if (remote) { +# if (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) + int delta = (dither)?dither_matrix[dst_y & 0xf][dst_x & 0xf]:127; + unsigned bright = brightness(color); + bright = (3 * bright + (bright >> 6) + delta) >> 8; + ((fb_remote_data *)bitmap)[dst_padded_width * (dst_y >> 3)+dst_x] |= + remote_pattern[bright] << (dst_y & 7); +# endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ + } else +# endif /* defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 */ + { + int delta = (dither)?dither_matrix[dst_y & 0xf][dst_x & 0xf]:127; +# if LCD_DEPTH == 2 + unsigned bright = brightness(color); + bright = (3 * bright + (bright >> 6) + delta) >> 8; +# if LCD_PIXELFORMAT == VERTICAL_PACKING + /* iriver H1x0 */ + ((fb_data *)bitmap)[dst_padded_width * (dst_y >> 2)+dst_x] |= (~bright & 3) << (2 * (dst_y & 3)); +# else /* LCD_PIXELFORMAT == HORIZONTAL_PACKING */ + /* greyscale iPods */ + ((fb_data *)bitmap)[dst_padded_width * dst_y + (dst_x >> 2)] |= (~bright & 3) << (6 - 2 * (dst_x & 3)); +# endif /* LCD_PIXELFORMAT */ +# elif LCD_DEPTH == 16 + /* iriver h300, colour iPods, X5 */ + unsigned r = (31 * color.red + (color.red >> 3) + delta) >> 8; + unsigned g = (63 * color.green + (color.green >> 2) + delta) >> 8; + unsigned b = (31 * color.blue + (color.blue >> 3) + delta) >> 8; + ((fb_data *)bitmap)[dst_padded_width * dst_y + dst_x] = LCD_RGBPACK_LCD(r, g, b); +# endif /* LCD_DEPTH */ + } + } else +#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ + { + if (brightness(color) < 128) + bitmap[dst_padded_width * (dst_y >> 3)+dst_x] |= 1 << (dst_y & 7); + } +} + /****************************************************************************** * read_bmp_file() * * Reads a BMP file and puts the data in rockbox format in *bitmap. * *****************************************************************************/ -int read_bmp_file(char* filename, +int read_bmp_fd( int fd, struct bitmap *bm, int maxsize, - int format) + int format, + int dst_maxwidth, + int dst_maxheight, + int flags) { struct bmp_header bmph; - int width, height, padded_width; - int dst_height, dst_width; - int fd, row, col, ret; + int src_w, src_h, src_padded_width; + int dst_w, dst_h, dst_padded_width, dst_padded_height; + int row, ret; + int fact_w = FACT_SCALE; + int fact_h = FACT_SCALE; + int top = 0,decrement_y = 0; + int left = 0,decrement_x = 0; int depth, numcolors, compression, totalsize; unsigned char *bitmap = bm->data; - uint32_t bmpbuf[LCD_WIDTH]; /* Buffer for one line */ - uint32_t palette[256]; + union rgb_union palette[256]; + bool dither = false; #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) bool transparent = false; - bool dither = false; #ifdef HAVE_REMOTE_LCD bool remote = false; - + if (format & FORMAT_REMOTE) { remote = true; #if LCD_REMOTE_DEPTH == 1 @@ -182,41 +250,133 @@ } #else - (void)format; + format = FORMAT_MONO; #endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ - fd = open(filename, O_RDONLY); - - /* Exit if file opening failed */ - if (fd < 0) { - DEBUGF("read_bmp_file: can't open '%s', rc: %d\n", filename, fd); - return fd * 10 - 1; - } - /* read fileheader */ ret = read(fd, &bmph, sizeof(struct bmp_header)); if (ret < 0) { - close(fd); return ret * 10 - 2; } if (ret != sizeof(struct bmp_header)) { DEBUGF("read_bmp_file: can't read BMP header."); - close(fd); return -3; } - width = readlong(&bmph.width); - if (width > LCD_WIDTH) { - DEBUGF("read_bmp_file: Bitmap too wide (%d pixels, max is %d)\n", - width, LCD_WIDTH); - close(fd); + /* check bitmap file signature */ + if (readshort(&bmph.type)!=0x4d42) { + DEBUGF("read_bmp_file: wring signature."); + return -3; + } + /* read the source dimensions */ + src_w = readlong(&bmph.width); + src_h = readlong(&bmph.height); + + /* Exit if too wide */ + if (((flags & BMP_RESIZE_NONE)==0 && (src_w > MAX_WIDTH)) || ((flags & BMP_RESIZE_NONE) && (src_w > LCD_WIDTH))) { + DEBUGF("read_bmp_file: Bitmap is too wide (%d pixels, max is %d)\n", + src_w, MAX_WIDTH); return -4; } - height = readlong(&bmph.height); + /* Calculate resize factors and image size */ + if (dst_maxwidth<=0 || dst_maxheight<=0) + { + dst_w = src_w; + dst_h = src_h; + } + else + { + dst_w = dst_maxwidth; + dst_h = dst_maxheight; + if (flags & BMP_RESIZE_FILL) + { + int aspect_src = src_w*FACT_SCALE/src_h; + int aspect_dst = dst_maxwidth*FACT_SCALE/dst_maxheight; + if (aspect_dst >= aspect_src) + { /* adjust height */ + decrement_y = (dst_maxheight * aspect_dst / aspect_src) - dst_maxheight; + } + else + { /* adjust width */ + decrement_x = (dst_maxwidth * aspect_src / aspect_dst) - dst_maxwidth; + } + if ((flags & BMP_RESIZE_INCREASE)==0 && + (src_w 0) { + fact = (src_w * FACT_SCALE) / (dst_w + decrement_x); + if ((fact < FACT_SCALE && (flags & BMP_RESIZE_DECREASE)) || /* decrease allowed */ + (fact > FACT_SCALE && (flags & BMP_RESIZE_INCREASE))) /* increase allowed */ + fact_w = fact; + } + + if (dst_h > 0) { + fact = (src_h * FACT_SCALE) / (dst_h + decrement_y); + if ((fact < FACT_SCALE && (flags & BMP_RESIZE_DECREASE)) || /* decrease allowed */ + (fact > FACT_SCALE && (flags & BMP_RESIZE_INCREASE))) /* increase allowed */ + fact_h = fact; + } + + if ((flags & BMP_RESIZE_IGNORE_ASPECT)==0) + { /* keep aspect ratio of source */ + if (fact_wwidth = dst_w; + bm->height = dst_h; + + DEBUGF("read_bmp_file: src=%d/%d, dst=%d/%d max=%d/%d (fact=%d/%d) dec=%d/%d\n", + src_w, src_h, + dst_w, dst_h, + dst_maxwidth, dst_maxheight, + fact_w, fact_h, + decrement_x,decrement_y); + + /* Exit if too big for screen */ + if ((flags & BMP_RESIZE_NONE)==0 && (dst_w > LCD_WIDTH || dst_h > LCD_HEIGHT)) { + DEBUGF("read_bmp_file: error - Bitmap is too big (%d x %d pixels, screen is %d x %d)\n", + dst_w, dst_h, LCD_WIDTH, LCD_HEIGHT); + return -5; + } + depth = readshort(&bmph.bit_count); - padded_width = ((width * depth + 31) >> 3) & ~3; /* 4-byte boundary aligned */ + src_padded_width = ((src_w * depth + 31) >> 3) & ~3; /* 4-byte boundary aligned */ #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) if (format == FORMAT_ANY) { @@ -226,50 +386,44 @@ format = FORMAT_NATIVE; } bm->format = format; -#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ - /* returning image size */ - bm->width = width; - bm->height = height; - -#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) if (format == FORMAT_NATIVE) { -#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 +# if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 if (remote) { -#if (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) - dst_width = width; - dst_height = (height + 7) >> 3; -#endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ - totalsize = dst_width * dst_height * sizeof(fb_remote_data); +# if (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) + dst_padded_width = dst_w; + dst_padded_height = (dst_h + 7) >> 3; +# endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ + totalsize = dst_padded_width * dst_padded_height * sizeof(fb_remote_data); } else -#endif /* defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 */ +# endif /* defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 */ { -#if LCD_DEPTH == 2 -#if LCD_PIXELFORMAT == VERTICAL_PACKING - dst_width = width; - dst_height = (height + 3) >> 2; -#else /* LCD_PIXELFORMAT == HORIZONTAL_PACKING */ - dst_width = (width + 3) >> 2; - dst_height = height; -#endif /* LCD_PIXELFORMAT */ -#elif LCD_DEPTH == 16 - dst_width = width; - dst_height = height; -#endif /* LCD_DEPTH */ - totalsize = dst_width * dst_height * sizeof(fb_data); +# if LCD_DEPTH == 2 +# if LCD_PIXELFORMAT == VERTICAL_PACKING + dst_padded_width = dst_w; + dst_padded_height = (dst_h + 3) >> 2; +# else /* LCD_PIXELFORMAT == HORIZONTAL_PACKING */ + dst_padded_width = (dst_w + 3) >> 2; + dst_padded_height = dst_h; +# endif /* LCD_PIXELFORMAT */ +# elif LCD_DEPTH == 16 + dst_padded_width = dst_w; + dst_padded_height = dst_h; +# endif /* LCD_DEPTH */ + totalsize = dst_padded_width * dst_padded_height * sizeof(fb_data); } } else #endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ { - dst_width = width; - dst_height = (height + 7) >> 3; - totalsize = dst_width * dst_height; + dst_padded_width = dst_w; + dst_padded_height = (dst_h + 7) >> 3; + totalsize = dst_padded_width * dst_padded_height; } /* Check if this fits the buffer */ if (totalsize > maxsize) { - DEBUGF("read_bmp_file: Bitmap too large for buffer: " - "%d bytes.\n", totalsize); - close(fd); + DEBUGF("read_bmp_file: error - Bitmap is too large to fit the supplied buffer: " + "%d bytes.%d:%d\n", (dst_padded_height * dst_padded_width), + totalsize, maxsize); return -6; } @@ -280,13 +434,12 @@ numcolors = 1 << depth; } else numcolors = (compression == 3) ? 3 : 0; - + if (numcolors > 0 && numcolors <= 256) { if (read(fd, palette, numcolors * sizeof(uint32_t)) != numcolors * (int)sizeof(uint32_t)) { DEBUGF("read_bmp_file: Can't read color palette\n"); - close(fd); return -7; } } @@ -322,7 +475,6 @@ if (compression != 0) { /* not BI_RGB */ DEBUGF("read_bmp_file: Unsupported compression (type %d)\n", compression); - close(fd); return -8; } break; @@ -333,32 +485,54 @@ memset(bitmap, 0, totalsize); - /* loop to read rows and put them to buffer */ - for (row = height - 1; row >= 0; row--) { - unsigned data, mask; - unsigned char *p; - uint16_t *p2; - uint32_t *rp; - union rgb_union *qp; - union rgb_union q0, q1; +#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 + if (remote) + format |= FORMAT_REMOTE; // set remote flag if it is necessary +#endif + /* loop to read rows and put them to buffer */ + for (row = src_h - 1; row >= 0; row--) { /* read one row */ - ret = read(fd, bmpbuf, padded_width); - if (ret != padded_width) { + int dst_y, num_rows = 0; + if (fact_h >= FACT_SCALE) { + /* decrease vertically or just transport the pixels + * -> put only every "fact" row to the dest buffer */ + if ((row % (fact_h / FACT_SCALE)) == 0) { + dst_y = (row * FACT_SCALE) / fact_h - top; + if (dst_y>=0 && dst_y duplicate src-pixel for not existing dst_rows/cols */ + dst_y = MAX(row * FACT_SCALE / fact_h - top,0); + num_rows = MIN((row + 1) * FACT_SCALE / fact_h - top, dst_h)-dst_y; + } + if (num_rows<=0) { + /* if we are not going to process data - just skip reading */ + ret = lseek(fd, src_padded_width, SEEK_CUR ); + continue; + } + ret = read(fd, bmpbuf, src_padded_width); + if (ret != src_padded_width) { DEBUGF("read_bmp_file: error reading image, read returned: %d " - "expected: %d\n", ret, padded_width); - close(fd); + "expected: %d\n", ret, src_padded_width); return -9; } + unsigned data, mask; + unsigned char *p; + uint16_t *p2; + uint32_t *rp; + union rgb_union q0, q1; /* convert whole line in-place to XRGB8888 (little endian) */ - rp = bmpbuf + width; + rp = (uint32_t *)(bmpbuf + src_w); switch (depth) { case 1: - q0.raw = palette[0]; - q1.raw = palette[1]; - p = (unsigned char*)bmpbuf + ((width + 7) >> 3); - mask = 0x80 >> ((width + 7) & 7); + q0.raw = palette[0].raw; + q1.raw = palette[1].raw; + p = (unsigned char*)bmpbuf + ((src_w + 7) >> 3); + mask = 0x80 >> ((src_w + 7) & 7); while (p > (unsigned char*)bmpbuf) { data = *(--p); for (; mask <= 0x80; mask <<= 1) @@ -368,25 +542,25 @@ break; case 4: - if (width & 1) + if (src_w & 1) rp++; - p = (unsigned char*)bmpbuf + ((width + 1) >> 1); + p = (unsigned char*)bmpbuf + ((src_w + 1) >> 1); while (p > (unsigned char*)bmpbuf) { data = *(--p); - *(--rp) = palette[data & 0x0f]; - *(--rp) = palette[data >> 4]; + *(--rp) = palette[data & 0x0f].raw; + *(--rp) = palette[data >> 4].raw; } break; case 8: - p = (unsigned char*)bmpbuf + width; + p = (unsigned char*)bmpbuf + src_w; while (p > (unsigned char*)bmpbuf) - *(--rp) = palette[*(--p)]; + *(--rp) = palette[*(--p)].raw; break; - + case 15: case 16: - p2 = (uint16_t *)bmpbuf + width; + p2 = (uint16_t *)bmpbuf + src_w; while (p2 > (uint16_t *)bmpbuf) { unsigned component, rgb; @@ -432,7 +606,7 @@ break; case 24: - p = (unsigned char*)bmpbuf + 3 * width; + p = (unsigned char*)bmpbuf + 3 * src_w; while (p > (unsigned char*)bmpbuf) { data = *(--p); data = (data << 8) | *(--p); @@ -444,101 +618,56 @@ case 32: /* already in desired format */ break; } - + /* Convert to destination format */ - qp = (union rgb_union *)bmpbuf; -#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) - if (format == FORMAT_NATIVE) { -#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 - if (remote) { -#if (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) - fb_remote_data *dest = (fb_remote_data *)bitmap - + dst_width * (row >> 3); - int shift = row & 7; - int delta = 127; - unsigned bright; - - for (col = 0; col < width; col++) { - if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; - bright = brightness(*qp++); - bright = (3 * bright + (bright >> 6) + delta) >> 8; - *dest++ |= remote_pattern[bright] << shift; - } -#endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ - } else -#endif /* defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 */ - { -#if LCD_DEPTH == 2 -#if LCD_PIXELFORMAT == VERTICAL_PACKING - /* iriver H1x0 */ - fb_data *dest = (fb_data *)bitmap + dst_width * (row >> 2); - int shift = 2 * (row & 3); - int delta = 127; - unsigned bright; - - for (col = 0; col < width; col++) { - if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; - bright = brightness(*qp++); - bright = (3 * bright + (bright >> 6) + delta) >> 8; - *dest++ |= (~bright & 3) << shift; - } -#else /* LCD_PIXELFORMAT == HORIZONTAL_PACKING */ - /* greyscale iPods */ - fb_data *dest = (fb_data *)bitmap + dst_width * row; - int shift = 6; - int delta = 127; - unsigned bright; - unsigned data = 0; - - for (col = 0; col < width; col++) { - if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; - bright = brightness(*qp++); - bright = (3 * bright + (bright >> 6) + delta) >> 8; - data |= (~bright & 3) << shift; - shift -= 2; - if (shift < 0) { - *dest++ = data; - data = 0; - shift = 6; - } - } - if (shift < 6) - *dest++ = data; -#endif /* LCD_PIXELFORMAT */ -#elif LCD_DEPTH == 16 - /* iriver h300, colour iPods, X5 */ - fb_data *dest = (fb_data *)bitmap + dst_width * row; - int delta = 127; - unsigned r, g, b; - - for (col = 0; col < width; col++) { - if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; - q0 = *qp++; - r = (31 * q0.red + (q0.red >> 3) + delta) >> 8; - g = (63 * q0.green + (q0.green >> 2) + delta) >> 8; - b = (31 * q0.blue + (q0.blue >> 3) + delta) >> 8; - *dest++ = LCD_RGBPACK_LCD(r, g, b); - } -#endif /* LCD_DEPTH */ - } - } else -#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ - { - p = bitmap + dst_width * (row >> 3); - mask = 1 << (row & 7); - - for (col = 0; col < width; col++, p++) - if (brightness(*qp++) < 128) - *p |= mask; - } + while (--num_rows>=0) { + int src_col, dst_x; + if (fact_w >= FACT_SCALE) + { /* decrease horizontally or just transport the pixels */ + for (dst_x = 0; dst_x < dst_w; dst_x++) { + src_col = ((dst_x + left) * fact_w) / FACT_SCALE; + set_pixel(format,bitmap,dither,bmpbuf[src_col],dst_padded_width,dst_x,dst_y); + } + } + else /*if (fact_w < FACT_SCALE)*/ + { /* increase horizontally */ + for (src_col = 0; src_col < src_w; src_col++) { + int num_col; + dst_x = MAX(src_col * FACT_SCALE / fact_w - left,0); + num_col = MIN((src_col + 1) * FACT_SCALE / fact_w - left,dst_w) - dst_x; + q0 = bmpbuf[src_col]; + while (--num_col>=0) { + set_pixel(format,bitmap,dither,q0,dst_padded_width,dst_x,dst_y); + dst_x++; + } + } + } + dst_y++; + } } - close(fd); + DEBUGF("read_bmp_file: totalsize=%d, width=%d, height=%d depth=%d compression=%d numcolors=%d\n", + totalsize, dst_w, dst_h, depth, compression, numcolors); - DEBUGF("totalsize: %d\n", totalsize); return totalsize; /* return the used buffer size. */ } + +int read_bmp_file(char* filename, + struct bitmap *bm, + int maxsize, + int format, + int dst_maxwidth, + int dst_maxheight, + int flags) +{ + int fd = open(filename, O_RDONLY); + int res; + /* Exit if file opening failed */ + if (fd < 0) { + DEBUGF("read_bmp_file: can't open '%s', rc: %d\n", filename, fd); + return fd * 10 - 1; + } + res = read_bmp_fd(fd, bm, maxsize, format, dst_maxwidth, dst_maxheight, flags); + close(fd); + return res; +} Index: apps/recorder/bmp.h =================================================================== --- apps/recorder/bmp.h (revision 14627) +++ apps/recorder/bmp.h (working copy) @@ -22,6 +22,12 @@ #include "config.h" #include "lcd.h" +#define BMP_RESIZE_NONE 1 +#define BMP_RESIZE_INCREASE 2 +#define BMP_RESIZE_DECREASE 4 +#define BMP_RESIZE_IGNORE_ASPECT 8 +#define BMP_RESIZE_FILL 16 + /********************************************************************* * read_bmp_file() * @@ -30,8 +36,20 @@ * Returns < 0 for error, or number of bytes used from the bitmap buffer * **********************************************/ +int read_bmp_fd (int fd, + struct bitmap *bm, + int maxsize, + int format, + int dst_maxwidth, /* = 0 */ + int dst_maxheight,/* = 0 */ + int flags); /* BMP_RESIZE_NONE */ + int read_bmp_file(char* filename, struct bitmap *bm, int maxsize, - int format); + int format, + int dst_maxwidth, /* = 0 */ + int dst_maxheight,/* = 0 */ + int flags); /* BMP_RESIZE_NONE */ + #endif Index: apps/tree.c =================================================================== --- apps/tree.c (revision 14627) +++ apps/tree.c (working copy) @@ -1068,8 +1068,14 @@ pltick = current_tick; +#ifdef SANSA_E200 snprintf(filename, sizeof filename, "%s.m3u8", + tc.currdir[1] ? tc.currdir : "/Playlists/root"); +#else + snprintf(filename, sizeof filename, "%s.m3u8", tc.currdir[1] ? tc.currdir : "/root"); +#endif + FOR_NB_SCREENS(i) { gui_textarea_clear(&screens[i]); Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang (revision 14627) +++ apps/lang/english.lang (working copy) @@ -11242,3 +11242,143 @@ usbstack: "Device Driver" + + id: LANG_SET_AS_SPLASH + desc: in context_menu + user: + + *: "Set Splash Screen" + + + *: "Set Splash Screen" + + + *: "Set Splash Screen" + + + + id: LANG_SHOW_SPLASH + desc: in settings_menu + user: + + *: "Configure Splash Screen" + + + *: "Configure Splash Screen" + + + *: "Configure Splash Screen" + + + + id: VOICE_WHITE + desc: spoken only, for announcing chess piece color + user: + + *: "" + + + *: "" + + + *: "White" + + + + id: VOICE_BLACK + desc: spoken only, for announcing chess piece color + user: + + *: "" + + + *: "" + + + *: "Black" + + + + id: VOICE_PAWN + desc: spoken only, for announcing chess piece names + user: + + *: "" + + + *: "" + + + *: "Pawn" + + + + id: VOICE_KNIGHT + desc: spoken only, for announcing chess piece names + user: + + *: "" + + + *: "" + + + *: "Knight" + + + + id: VOICE_BISHOP + desc: spoken only, for announcing chess piece names + user: + + *: "" + + + *: "" + + + *: "Bishop" + + + + id: VOICE_ROOK + desc: spoken only, for announcing chess piece names + user: + + *: "" + + + *: "" + + + *: "Rook" + + + + id: VOICE_QUEEN + desc: spoken only, for announcing chess piece names + user: + + *: "" + + + *: "" + + + *: "Queen" + + + + id: VOICE_KING + desc: spoken only, for announcing chess piece names + user: + + *: "" + + + *: "" + + + *: "King" + + Index: apps/plugins/pong.c =================================================================== --- apps/plugins/pong.c (revision 14627) +++ apps/plugins/pong.c (working copy) @@ -30,6 +30,8 @@ #define SPEEDX ( LCD_WIDTH * 3 ) / 2 /* Recorder: 168 iRiver: 240 */ #define SPEEDY LCD_HEIGHT * 2 /* Recorder: 128 iRiver: 256 */ +#define CPU_PLAYER_DIST ( (LCD_WIDTH/8 ) * 5 ) /* This is the width of the dead spot where the */ +#define DEM_PLAYER_DIST ( (LCD_WIDTH/8 ) * 3 ) /* cpu player doesnt care about the ball -- 3/8 of the screen */ #define RES 100 @@ -44,14 +46,6 @@ #define PONG_RIGHT_UP BUTTON_F3 #define PONG_RIGHT_DOWN BUTTON_RIGHT -#elif CONFIG_KEYPAD == ARCHOS_AV300_PAD -#define PONG_QUIT BUTTON_OFF -#define PONG_PAUSE BUTTON_ON -#define PONG_LEFT_UP BUTTON_F1 -#define PONG_LEFT_DOWN BUTTON_LEFT -#define PONG_RIGHT_UP BUTTON_F3 -#define PONG_RIGHT_DOWN BUTTON_RIGHT - #elif CONFIG_KEYPAD == ONDIO_PAD #define PONG_QUIT BUTTON_OFF #define PONG_PAUSE BUTTON_RIGHT @@ -126,12 +120,55 @@ int e_pad[2]; /* existing current Y positions of pads */ int ballspeedx; /* */ int ballspeedy; /* */ - int score[2]; + bool cpu_player[2]; /* Status of AI players */ }; void singlepad(int x, int y, int set) { + +#ifdef HAVE_LCD_COLOR + +#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) /* Get the peaks. ( Borrowed from vu_meter ) */ + int left_peak = rb->mas_codec_readreg(0xC); + int right_peak = rb->mas_codec_readreg(0xD); +#elif (CONFIG_CODEC == SWCODEC) + int left_peak, right_peak; + rb->pcm_calculate_peaks(&left_peak, &right_peak); +#endif + + int left_boost, right_boost; + + left_peak = left_peak/0x80; /* Devide peak data by 128 to bring the max value down from ~32k to 256 */ + right_peak = right_peak/0x80; + left_boost = 0; + right_boost = 0; + + if(left_peak<0x80) /* Make sure the pads dont black out with soft music */ + left_peak = 0x80; + if(right_peak<0x80) + right_peak = 0x80; + + if(left_peak>0xFF) /* And make sure they dont go over 255 */ + left_peak = 0xFF; + if(right_peak>0xFF) + right_peak = 0xFF; + + if(left_peak>0xDD) /* Boost the red when needed to make keep loud sounds bright, */ + left_boost = left_peak - 0xDD; /* instead of just very green. */ + if(right_peak>0xDD) + right_boost = right_peak - 0xDD; + + + + if(xlcd_set_foreground(LCD_RGBPACK( 64 + left_boost, left_peak, 0)); + }else{ + rb->lcd_set_foreground(LCD_RGBPACK( 64 + right_boost, right_peak, 0)); + } + +#endif /* HAVE_LCD_COLOR */ + if(set) { rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT); } @@ -162,11 +199,11 @@ the wall */ if(pad) { /* right-side */ - if(p->ballx > LCD_WIDTH*RES) + if(p->ballx > ( LCD_WIDTH*RES ) - PAD_WIDTH ) return true; } else { - if(p->ballx < 0) + if(p->ballx < PAD_WIDTH) return true; } return false; @@ -210,8 +247,14 @@ void bounce(struct pong *p, int pad, int info) { - (void)pad; /* not used right now */ + p->ballspeedx = -p->ballspeedx; + + if(pad==0){ /* Give ball a little push to keep it from getting stuck between wall and pad */ + p->ballx += PAD_WIDTH; + }else{ + p->ballx -= PAD_WIDTH; + } /* info is the hit-angle into the pad */ if(p->ballspeedy > 0) { @@ -281,6 +324,7 @@ int info; /* movement */ + p->ballx += p->ballspeedx; p->bally += p->ballspeedy; @@ -318,6 +362,10 @@ rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); rb->lcd_fillrect(x, y, BALL_WIDTH, BALL_HEIGHT); rb->lcd_set_drawmode(DRMODE_SOLID); + +#ifdef HAVE_LCD_COLOR + rb->lcd_set_foreground(LCD_RGBPACK(128, 255, 0)); +#endif /* draw the new ball position */ rb->lcd_fillrect(newx, newy, BALL_WIDTH, BALL_HEIGHT); @@ -366,19 +414,56 @@ #endif key = rb->button_status(); /* ignore BUTTON_REPEAT */ - - if(key & PONG_LEFT_DOWN) /* player left goes down */ + + if(p->cpu_player[1] == true){ + + if( (p->bally/RES > p->w_pad[0]) & (p->ballx/RES < DEM_PLAYER_DIST) ) /* player right goes down */ padmove(&p->w_pad[0], MOVE_STEP); - if(key & PONG_LEFT_UP) /* player left goes up */ + if( (p->bally/RES < p->w_pad[0]) & (p->ballx/RES < DEM_PLAYER_DIST) ) /* player right goes up */ padmove(&p->w_pad[0], -MOVE_STEP); + + if( (key & PONG_LEFT_DOWN) || (key & PONG_LEFT_UP) ){ /* if left player presses control keys stop cpu player */ + p->cpu_player[1] = false; + p->score[0] = p->score[1] = 0; /* reset the score */ + rb->lcd_clear_display(); /* get rid of the text */ + }; + + }else{ + + if(key & PONG_LEFT_DOWN) /* player left goes down */ + padmove(&p->w_pad[0], MOVE_STEP); - if(key & PONG_RIGHT_DOWN) /* player right goes down */ - padmove(&p->w_pad[1], MOVE_STEP); + if(key & PONG_LEFT_UP) /* player left goes up */ + padmove(&p->w_pad[0], -MOVE_STEP); + + } + - if(key & PONG_RIGHT_UP) /* player right goes up */ - padmove(&p->w_pad[1], -MOVE_STEP); + if(p->cpu_player[2] == true){ + + if( (p->bally/RES > p->w_pad[1]) & (p->ballx/RES > CPU_PLAYER_DIST) ) /* player right goes down */ + padmove(&p->w_pad[1], MOVE_STEP); + + if( (p->bally/RES < p->w_pad[1]) & (p->ballx/RES > CPU_PLAYER_DIST) ) /* player right goes up */ + padmove(&p->w_pad[1], -MOVE_STEP); + + if( (key & PONG_RIGHT_DOWN) || (key & PONG_RIGHT_UP) ){ /* if right player presses control keys stop cpu player */ + p->cpu_player[2] = false; + p->score[0] = p->score[1] = 0; /* reset the score */ + rb->lcd_clear_display(); /* get rid of the text */ + }; + + }else{ + + if(key & PONG_RIGHT_DOWN) /* player right goes down */ + padmove(&p->w_pad[1], MOVE_STEP); + + if(key & PONG_RIGHT_UP) /* player right goes up */ + padmove(&p->w_pad[1], -MOVE_STEP); + }; + if(rb->default_event_handler(key) == SYS_USB_CONNECTED) return -1; /* exit game because of USB */ } @@ -390,16 +475,39 @@ static char buffer[20]; int w; +#ifdef HAVE_LCD_COLOR + rb->lcd_set_foreground(LCD_RGBPACK(128, 255, 0)); +#endif + rb->snprintf(buffer, sizeof(buffer), "%d - %d", p->score[0], p->score[1]); w = rb->lcd_getstringsize((unsigned char *)buffer, NULL, NULL); rb->lcd_putsxy( (LCD_WIDTH / 2) - (w / 2), 0, (unsigned char *)buffer); } +void blink_demo(void) +{ + static char buffer[30]; + int w; + + #ifdef HAVE_LCD_COLOR + rb->lcd_set_foreground(LCD_RGBPACK(128, 255, 0)); + #endif + + rb->snprintf(buffer, sizeof(buffer), "Press Key To Play"); + w = rb->lcd_getstringsize((unsigned char *)buffer, NULL, NULL); + if(LCD_WIDTH > ( (w/8)*7 ) ) /* make sure text isn't too long for screen */ + rb->lcd_putsxy( (LCD_WIDTH / 2) - (w / 2), (LCD_HEIGHT / 2), (unsigned char *)buffer); +} + /* this is the plugin entry point */ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { struct pong pong; int game = 1; + + int blink_timer = 0; + int blink_rate = 20; + bool blink = true; /* init the struct with some silly values to start with */ @@ -410,6 +518,7 @@ pong.w_pad[0] = 7; pong.e_pad[1] = 0; pong.w_pad[1] = 40; + pong.cpu_player[1] = pong.cpu_player[2] = true; /* start every game in demo mode */ pong.ballspeedx = SPEEDX; pong.ballspeedy = SPEEDY; @@ -422,9 +531,14 @@ rb = api; /* use the "standard" rb pointer */ +#ifdef HAVE_LCD_COLOR + rb->lcd_set_background(LCD_RGBPACK(0, 0, 0)); + rb->lcd_set_foreground(LCD_RGBPACK(128, 255, 0)); +#endif + /* Clear screen */ rb->lcd_clear_display(); - + /* go go go */ while(game > 0) { if (game == 2) { /* Game Paused */ @@ -433,6 +547,31 @@ game = keys(&pong); /* short circuit */ rb->lcd_clear_display(); } + + if( (pong.cpu_player[1]==true) && (pong.cpu_player[2]==true) ){ + + if(blink_timerlcd_clear_display(); + } + + } + showscore(&pong); pad(&pong, 0); /* draw left pad */ pad(&pong, 1); /* draw right pad */ Index: apps/plugins/CATEGORIES =================================================================== --- apps/plugins/CATEGORIES (revision 14627) +++ apps/plugins/CATEGORIES (working copy) @@ -1,4 +1,14 @@ +tictactoe,games +language_quiz,apps +contact,apps +espeak,apps +autostart,apps +splash,apps +Playlist_Converter,viewers +HTML_Parser,viewers +Delete_Playlist,viewers alpine_cdc,apps +splash,apps battery_bench,apps blackjack,games bounce,demos Index: apps/plugins/viewers.config =================================================================== --- apps/plugins/viewers.config (revision 14627) +++ apps/plugins/viewers.config (working copy) @@ -1,3 +1,11 @@ +jdic,language_quiz,- +-,apps/contact,5 +-,apps/espeak,5 +-,apps/splash,11 +pla,viewers/Playlist_Converter,7 +html,viewers/HTML_Parser,5 +htm,viewers/HTML_Parser,5 +-,viewers/Delete-Playlist,5 ch8,viewers/chip8,0 txt,viewers/viewer,1 nfo,viewers/viewer,1 @@ -22,7 +30,7 @@ ss,games/sudoku,1 wav,viewers/wav2wv,- wav,viewers/mp3_encoder,- -wav,viewers/wavplay,9 +wav,viewers/wavplay,10 wav,viewers/wavview,10 wav,viewers/test_codec,- bmp,apps/rockpaint,11 Index: apps/plugins/chessbox/chessbox.c =================================================================== --- apps/plugins/chessbox/chessbox.c (revision 14627) +++ apps/plugins/chessbox/chessbox.c (working copy) @@ -20,6 +20,7 @@ ****************************************************************************/ #include "plugin.h" +#include "lang_enum.h" #ifdef HAVE_LCD_BITMAP @@ -357,8 +358,29 @@ rb->lcd_update(); } +static short oldx, oldy = 0; /* ---- Switch mark on board ---- */ void cb_switch ( short x , short y ) { + if (rb->talk_menus_enabled() && (x != oldx || y != oldy)) { + short c, r; + short l, piece, p_color; + + rb->do_shutup(); + cr2xy(x, y, &c, &r); + l = locn[r][c]; + piece = board[l]; + p_color = color[l]; + if (piece != no_piece) { + rb->talk_id (VOICE_WHITE + p_color, true); + if (piece >= pawn && piece <= king) { + rb->talk_id (VOICE_PAWN + piece - 1, true); + } + } + rb->talk_id (VOICE_CHAR_A + c, true); + rb->talk_id (VOICE_ONE + r, true); + oldx = x; + oldy = y; + } rb->lcd_set_drawmode ( DRMODE_COMPLEMENT ); rb->lcd_drawrect ( XOFS + x*TILE_WIDTH + 1 , YOFS + ( 7 - y )*TILE_HEIGHT +1 , @@ -994,12 +1016,13 @@ /* Add the ply to the PGN history (in algebraic notation) */ pgn_append_ply(rb, game, opponent, move_buffer, mate); + rb->talk_spell(move_buffer, true); rb->splash ( 0 , "Thinking..." ); #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost ( true ); #endif SelectMove ( computer , 0 , cb_wt_callback, move_buffer); -#ifdef HAVE_ADJUSTABLE_CPU_FREQ +#ifdef HAVE_ADJUSTABLE_CPU_FREQ1030G rb->cpu_boost ( false ); #endif /* Add the ply to the PGN history (in algebraic notation) and check @@ -1007,6 +1030,10 @@ */ if (move_buffer[0] != '\0'){ pgn_append_ply(rb, game, computer, move_buffer, mate); + rb->talk_id(VOICE_CHAR_A + move_buffer[0] - 'a', false); + rb->talk_id(VOICE_ONE + move_buffer[1] - '1', true); + rb->talk_id(VOICE_CHAR_A + move_buffer[2] - 'a', true); + rb->talk_id(VOICE_ONE + move_buffer[3] - '1', true); } else { pgn_set_result(rb, game, mate); } Index: apps/plugins/mpegplayer/video_out.h =================================================================== --- apps/plugins/mpegplayer/video_out.h (revision 14627) +++ apps/plugins/mpegplayer/video_out.h (working copy) @@ -22,4 +22,6 @@ */ void vo_draw_frame (uint8_t * const * buf); +void vo_draw_frame_thumb (uint8_t * const * buf); void vo_setup (const mpeg2_sequence_t * sequence); +void vo_cleanup (void); Index: apps/plugins/mpegplayer/mpegplayer.c =================================================================== --- apps/plugins/mpegplayer/mpegplayer.c (revision 14627) +++ apps/plugins/mpegplayer/mpegplayer.c (working copy) @@ -110,6 +110,7 @@ #include "mpeg_settings.h" #include "video_out.h" #include "../../codecs/libmad/mad.h" +#include "splash.h" PLUGIN_HEADER PLUGIN_IRAM_DECLARE @@ -185,9 +186,7 @@ uint8_t* curr_packet_end; /* Current stream packet end */ uint8_t* prev_packet; /* Previous stream packet beginning */ - uint8_t* next_packet; /* Next stream packet beginning */ - - size_t guard_bytes; /* Number of bytes in guardbuf used */ + size_t prev_packet_length; /* Lenth of previous packet */ size_t buffer_remaining; /* How much data is left in the buffer */ uint32_t curr_pts; /* Current presentation timestamp */ uint32_t curr_time; /* Current time in samples */ @@ -318,10 +317,20 @@ /* NOTE: Putting the following variables in IRAM cause audio corruption on the ipod (reason unknown) */ -static uint8_t *disk_buf IBSS_ATTR; -static uint8_t *disk_buf_end IBSS_ATTR; -static uint8_t *disk_buf_tail IBSS_ATTR; -static size_t buffer_size IBSS_ATTR; +static uint8_t *disk_buf_start IBSS_ATTR; /* Start pointer */ +static uint8_t *disk_buf_end IBSS_ATTR; /* End of buffer pointer less + MPEG_GUARDBUF_SIZE. The + guard space is used to wrap + data at the buffer start to + pass continuous data + packets */ +static uint8_t *disk_buf_tail IBSS_ATTR; /* Location of last data + 1 + filled into the buffer */ +static size_t disk_buf_size IBSS_ATTR; /* The total buffer length + including the guard + space */ +static size_t file_remaining IBSS_ATTR; + #if NUM_CORES > 1 /* Some stream variables are shared between cores */ struct mutex stream_lock IBSS_ATTR; @@ -341,11 +350,29 @@ { } #endif -/* Events */ -static struct event_queue msg_queue IBSS_ATTR; +static int audio_sync_start IBSS_ATTR; /* If 0, the audio thread + yields waiting on the video + thread to synchronize with + the stream */ +static uint32_t audio_sync_time IBSS_ATTR; /* The time that the video + thread has reached after + synchronizing. The + audio thread now needs + to advance to this + time */ +static int video_sync_start IBSS_ATTR; /* While 0, the video thread + yields until the audio + thread has reached the + audio_sync_time */ +static int video_thumb_print IBSS_ATTR; /* If 1, the video thread is + only decoding one frame for + use in the menu. If 0, + normal operation */ +static int play_time IBSS_ATTR; /* The movie time as represented by + the maximum audio PTS tag in the + stream converted to minutes */ +char *filename; /* hack for resume time storage */ -#define MSG_BUFFER_NEARLY_EMPTY 1 -#define MSG_EXIT_REQUESTED 2 /* Various buffers */ /* TODO: Can we reduce the PCM buffer size? */ @@ -356,7 +383,7 @@ #define LIBMPEG2BUFFER_SIZE (2*1024*1024) /* 65536+6 is required since each PES has a 6 byte header with a 16 bit packet length field */ -#define MPEG_GUARDBUF_SIZE (64*1024+1024) /* Keep a bit extra - excessive for now */ +#define MPEG_GUARDBUF_SIZE (65*1024) /* Keep a bit extra - excessive for now */ #define MPEG_LOW_WATERMARK (1024*1024) static void pcm_playback_play_pause(bool play); @@ -477,9 +504,48 @@ ((p)[b3] << 6) | \ ((p)[b4] >> 2 ))) -/* This function demuxes the streams and gives the next stream data pointer */ -static void get_next_data( Stream* str ) +/* This function synchronizes the mpeg stream. The function returns + true on error */ +bool sync_data_stream(uint8_t **p) { + for (;;) + { + while ( !CMP_4_CONST(*p, PACK_START_CODE) && (*p) < disk_buf_tail ) + (*p)++; + if ( (*p) >= disk_buf_tail ) + break; + uint8_t *p_save = (*p); + if ( ((*p)[4] & 0xc0) == 0x40 ) /* mpeg-2 */ + (*p) += 14 + ((*p)[13] & 7); + else if ( ((*p)[4] & 0xf0) == 0x20 ) /* mpeg-1 */ + (*p) += 12; + else + (*p) += 5; + if ( (*p) >= disk_buf_tail ) + break; + if ( CMP_3_CONST(*p, PACKET_START_CODE_PREFIX) ) + { + (*p) = p_save; + break; + } + else + (*p) = p_save+1; + } + + if ( (*p) >= disk_buf_tail ) + return true; + else + return false; +} + +/* This function demuxes the streams and gives the next stream data + pointer. Type 0 is normal operation. Type 1 and 2 have been added + for rapid seeks into the data stream. Type 1 and 2 ignore the + video_sync_start state (a signal to yield for refilling the + buffer). Type 1 will append more data to the buffer tail (minumal + bufer size reads that are increased only as needed). */ +static int get_next_data( Stream* str, uint8_t type ) +{ uint8_t *p; uint8_t *header; int stream; @@ -487,30 +553,49 @@ static int mpeg1_skip_table[16] = { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - if (str->curr_packet_end == NULL) - { - /* What does this do? */ - while ((p = disk_buf) == NULL) - { - rb->lcd_putsxy(0,LCD_HEIGHT-10,"FREEZE!"); - rb->lcd_update(); - rb->sleep(HZ); - } - } - else - { - p = str->curr_packet_end; - } + if ( (p=str->curr_packet_end) == NULL) + p = disk_buf_start; while (1) { int length, bytes; - - if (p >= disk_buf_end) + + /* Yield for buffer filling */ + if ( (type == 0) && (str->buffer_remaining < 120*1024) && (file_remaining > 0) ) + while ( (str->buffer_remaining < 512*1024) && (file_remaining > 0) ) + rb->yield(); + + /* The packet start position (plus an arbitrary header length) + has exceeded the amount of data in the buffer */ + if ( type == 1 && (p+50) >= disk_buf_tail ) { - p = disk_buf + (p - disk_buf_end); + DEBUGF("disk buffer overflow\n"); + return 1; } + /* are we at the end of file? */ + { + size_t tmp_length; + if (p < str->prev_packet) + tmp_length = (disk_buf_end - str->prev_packet) + + (p - disk_buf_start); + else + tmp_length = (p - str->prev_packet); + if (0 == str->buffer_remaining-tmp_length-str->prev_packet_length) + { + str->curr_packet_end = str->curr_packet = NULL; + break; + } + } + + /* wrap the disk buffer */ + if (p >= disk_buf_end) + p = disk_buf_start + (p - disk_buf_end); + + /* wrap packet header if needed */ + if ( (p+50) >= disk_buf_end ) + rb->memcpy(disk_buf_end, disk_buf_start, 50); + /* Pack header, skip it */ if (CMP_4_CONST(p, PACK_START_CODE)) { @@ -527,7 +612,6 @@ rb->splash( 30, "Weird Pack header!" ); p += 5; } - /*rb->splash( 30, "Pack header" );*/ } /* System header, parse and skip it - four bytes */ @@ -541,29 +625,33 @@ p += header_length; - if (p >= disk_buf_end) - { - p = disk_buf + (p - disk_buf_end); - } - /*rb->splash( 30, "System header" );*/ + if ( p >= disk_buf_end ) + p = disk_buf_start + (p - disk_buf_end); } - + /* Packet header, parse it */ if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX)) { /* Problem */ - //rb->splash( HZ*3, "missing packet start code prefix : %X%X at %X", *p, *(p+2), p-disk_buf ); + rb->splash( HZ*3, "missing packet start code prefix : %X%X at %lX", + *p, *(p+2), (long unsigned int)(p-disk_buf_start) ); + + DEBUGF("end diff: %X,%X,%X,%X,%X,%X\n",(int)str->curr_packet_end, + (int)audio_str.curr_packet_end,(int)video_str.curr_packet_end, + (int)disk_buf_start,(int)disk_buf_end,(int)disk_buf_tail); + str->curr_packet_end = str->curr_packet = NULL; + + audio_sync_start=1; + video_sync_start=1; + break; - //++p; - //break; } /* We retrieve basic infos */ stream = p[3]; length = (p[4] << 8) | p[5]; - /*rb->splash( 100, "Stream : %X", stream );*/ if (stream != str->id) { /* End of stream ? */ @@ -624,12 +712,14 @@ break; } } - - if ((header[length - 1] & 0xc0) == 0x40) - { + + if ( (header[length - 1] & 0xc0) == 0x40 ) length += 2; - } + + + + len_skip = length; length += mpeg1_skip_table[header[length - 1] >> 4]; @@ -663,23 +753,17 @@ if (bytes > 0) { str->curr_packet_end = p + bytes; - //DEBUGF("prev = %d, curr = %d\n",str->prev_packet,str->curr_packet); if (str->curr_packet != NULL) { lock_stream(); + str->buffer_remaining -= str->prev_packet_length; if (str->curr_packet < str->prev_packet) - { - str->buffer_remaining -= (disk_buf_end - str->prev_packet) + - (str->curr_packet - disk_buf); - str->buffer_remaining -= str->guard_bytes; - str->guard_bytes = 0; - } + str->prev_packet_length = (disk_buf_end - str->prev_packet) + + (str->curr_packet - disk_buf_start); else - { - str->buffer_remaining -= (str->curr_packet - str->prev_packet); - } + str->prev_packet_length = (str->curr_packet - str->prev_packet); unlock_stream(); @@ -689,14 +773,12 @@ str->curr_packet = p; if (str->curr_packet_end > disk_buf_end) - { - str->guard_bytes = str->curr_packet_end - disk_buf_end; - rb->memcpy(disk_buf_end, disk_buf, str->guard_bytes); - } + rb->memcpy(disk_buf_end, disk_buf_start, str->curr_packet_end - disk_buf_end ); } break; } /* end while */ + return 0; } /* Our clock rate in ticks/second - this won't be a constant for long */ @@ -954,6 +1036,8 @@ int vol, minvol, maxvol; int button; + if (video_sync_start==1) { + if (str_have_msg(&audio_str)) { struct event ev; @@ -1025,6 +1109,7 @@ rb->lcd_setfont(FONT_SYSFIXED); if (result) { + settings.resume_time = (int)(get_stream_time()/44100/60); str_send_msg(&video_str, STREAM_QUIT, 0); audio_str.status = STREAM_STOPPED; } else { @@ -1035,6 +1120,7 @@ break; case MPEG_STOP: + settings.resume_time = (int)(get_stream_time()/44100/60); str_send_msg(&video_str, STREAM_QUIT, 0); audio_str.status = STREAM_STOPPED; break; @@ -1071,7 +1157,7 @@ audio_str.status = STREAM_STOPPED; } } - + } quit: return audio_str.status; } @@ -1097,8 +1183,24 @@ pcm_playback_play(0); /* Get first packet */ - get_next_data(&audio_str); + get_next_data(&audio_str, 0 ); + /* skip audio packets here */ + while (audio_sync_start==0) + { + audio_str.status = STREAM_PLAYING; + rb->yield(); + } + + if (audio_sync_time>10000) + { + while (TS_TO_TICKS(audio_str.curr_pts) < audio_sync_time - 10000) + { + get_next_data(&audio_str, 0 ); + rb->priority_yield(); + } + } + if (audio_str.curr_packet == NULL) goto done; @@ -1176,7 +1278,7 @@ mpabuf = mpa_buffer; /* Get data from next audio packet */ - get_next_data(&audio_str); + get_next_data(&audio_str, 0 ); } while (audio_str.curr_packet != NULL && mpabuf_used < MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD); @@ -1209,8 +1311,6 @@ if (mad_stat != 0) { - DEBUGF("Audio stream error - %d\n", stream.error); - if (stream.error == MAD_FLAG_INCOMPLETE || stream.error == MAD_ERROR_BUFLEN) { @@ -1270,6 +1370,12 @@ rb->priority_yield(); } + if (video_sync_start == 0 && pts->pts+(uint32_t)synth.pcm.lengthyield(); + } + /* TODO: This part will be replaced with dsp calls soon */ if (MAD_NCHANNELS(&frame.header) == 2) { @@ -1316,6 +1422,7 @@ audio_str.status = STREAM_PLAYING; pcmbuf_threshold = PCMBUF_PLAY_ALL; pcm_playback_seek_time(pcmbuf_tail->time); + video_sync_start = 1; } /* Make this data available to DMA */ @@ -1403,30 +1510,33 @@ /* Clear the display - this is mainly just to indicate that the video thread has started successfully. */ - rb->lcd_clear_display(); - rb->lcd_update(); + if (!video_thumb_print) + { + rb->lcd_clear_display(); + rb->lcd_update(); + } /* Request the first packet data */ - get_next_data( &video_str ); + get_next_data( &video_str, 0 ); if (video_str.curr_packet == NULL) - goto done; + goto video_thread_quit; mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end); total_offset += video_str.curr_packet_end - video_str.curr_packet; info = mpeg2_info (mpeg2dec); - /* Wait if the audio thread is buffering - i.e. before - the first frames are decoded */ - while (audio_str.status == STREAM_BUFFERING) - rb->priority_yield(); - while (1) { /* quickly check mailbox first */ - if (str_have_msg(&video_str)) + if (video_thumb_print) { + if (video_str.status == STREAM_STOPPED) + break; + } + else if (str_have_msg(&video_str)) + { while (1) { str_get_msg(&video_str, &ev); @@ -1460,7 +1570,8 @@ { case STATE_BUFFER: /* Request next packet data */ - get_next_data( &video_str ); + get_next_data( &video_str, 0 ); + mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end); total_offset += video_str.curr_packet_end - video_str.curr_packet; info = mpeg2_info (mpeg2dec); @@ -1468,7 +1579,7 @@ if (video_str.curr_packet == NULL) { /* No more data. */ - goto done; + goto video_thread_quit; } continue; @@ -1549,6 +1660,13 @@ period = TIME_TO_TICKS(info->sequence->frame_period); eta_video = curr_time; + + audio_sync_time = eta_video; + audio_sync_start = 1; + + while (video_sync_start == 0) + rb->yield(); + eta_audio = get_stream_time(); /* How early/late are we? > 0 = late, < 0 early */ @@ -1674,32 +1792,39 @@ picture_wait: /* Wait until audio catches up */ - while (eta_video > eta_audio) - { - rb->priority_yield(); - - /* Make sure not to get stuck waiting here forever */ - if (str_have_msg(&video_str)) + if (video_thumb_print) + video_str.status = STREAM_STOPPED; + else + while (eta_video > eta_audio) { - str_look_msg(&video_str, &ev); + rb->priority_yield(); + + /* Make sure not to get stuck waiting here forever */ + if (str_have_msg(&video_str)) + { + str_look_msg(&video_str, &ev); + + /* If not to play, process up top */ + if (ev.id != STREAM_PLAY) + goto rendering_finished; + + /* Told to play but already playing */ + str_get_msg(&video_str, &ev); + str_reply_msg(&video_str, 1); + } - /* If not to play, process up top */ - if (ev.id != STREAM_PLAY) - goto rendering_finished; - - /* Told to play but already playing */ - str_get_msg(&video_str, &ev); - str_reply_msg(&video_str, 1); + eta_audio = get_stream_time(); } - - eta_audio = get_stream_time(); - } - + picture_draw: /* Record last frame time */ last_render = *rb->current_tick; - vo_draw_frame(info->display_fbuf->buf); + if (video_thumb_print) + vo_draw_frame_thumb(info->display_fbuf->buf); + else + vo_draw_frame(info->display_fbuf->buf); + num_drawn++; picture_skip: @@ -1734,43 +1859,273 @@ rb->yield(); } -done: +video_thread_quit: flush_icache(); - video_str.status = STREAM_DONE; + mpeg2_close (mpeg2dec); - while (1) + /* Commit suicide */ + video_str.status = STREAM_TERMINATED; + rb->remove_thread(NULL); +} + +void initialize_stream( Stream *str, uint8_t *buffer_start, size_t disk_buf_len, int id ) +{ + str->curr_packet_end = str->curr_packet = NULL; + str->prev_packet_length = 0; + str->prev_packet = str->curr_packet_end = buffer_start; + str->buffer_remaining = disk_buf_len; + str->id = id; +} + +void display_thumb(int in_file) +{ + size_t disk_buf_len; + + video_thumb_print = 1; + audio_sync_start = 1; + video_sync_start = 1; + + disk_buf_len = rb->read (in_file, disk_buf_start, disk_buf_size - MPEG_GUARDBUF_SIZE); + disk_buf_tail = disk_buf_start + disk_buf_len; + initialize_stream(&video_str,disk_buf_start,disk_buf_len,0xe0); + + video_str.status = STREAM_PLAYING; + + if ((video_str.thread = rb->create_thread(video_thread, + (uint8_t*)video_stack,VIDEO_STACKSIZE,"mpgvideo" + IF_PRIO(,PRIORITY_PLAYBACK) + IF_COP(, COP, true))) == NULL) { - str_get_msg(&video_str, &ev); + rb->splash(HZ, "Cannot create video thread!"); + } + else + { + while (video_str.status != STREAM_TERMINATED) + rb->yield(); + } +} - if (ev.id == STREAM_QUIT) - break; +int find_length( int in_file ) +{ + uint8_t *p; + size_t read_length = 60*1024; + size_t disk_buf_len; + + play_time = 0; - str_reply_msg(&video_str, 0); + /* temporary read buffer size cannot exceed buffer size */ + if ( read_length > disk_buf_size ) + read_length = disk_buf_size; + + /* read tail of file */ + rb->lseek( in_file, -1*read_length, SEEK_END ); + disk_buf_len = rb->read( in_file, disk_buf_start, read_length ); + disk_buf_tail = disk_buf_start + disk_buf_len; + + /* sync reader to this segment of the stream */ + p=disk_buf_start; + if (sync_data_stream(&p)) + { + DEBUGF("Could not sync stream\n"); + return PLUGIN_ERROR; } + + /* find last PTS in audio stream; will movie always have audio? if + the play time can not be determined, set play_time to 0 */ + audio_sync_start = 0; + audio_sync_time = 0; + video_sync_start = 0; + { + Stream tmp; + initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); + + do + { + get_next_data(&tmp, 2); + if (tmp.tagged == 1) + /* 10 sec less to insure the video frame exist */ + play_time = (int)((tmp.curr_pts/45000-10)/60); + } + while (tmp.curr_packet_end != NULL); + } + return 0; +} -video_thread_quit: - flush_icache(); +ssize_t seek_PTS( int in_file, int start_time, int accept_button ) +{ + static ssize_t last_seek_pos = 0; + static int last_start_time = 0; + ssize_t seek_pos; + size_t disk_buf_len; + uint8_t *p; + size_t read_length = 60*1024; - /* Commit suicide */ - video_str.status = STREAM_TERMINATED; - rb->remove_thread(NULL); + /* temporary read buffer size cannot exceed buffer size */ + if ( read_length > disk_buf_size ) + read_length = disk_buf_size; + + if ( start_time == last_start_time ) + { + seek_pos = last_seek_pos; + rb->lseek(in_file,seek_pos,SEEK_SET); + } + else if ( start_time != 0 ) + { + seek_pos = rb->filesize(in_file)*start_time/play_time; + int seek_pos_sec_inc = rb->filesize(in_file)/play_time/60; + + if (seek_pos<0) + seek_pos=0; + if ((size_t)seek_pos > rb->filesize(in_file) - read_length) + seek_pos = rb->filesize(in_file) - read_length; + rb->lseek( in_file, seek_pos, SEEK_SET ); + disk_buf_len = rb->read( in_file, disk_buf_start, read_length ); + disk_buf_tail = disk_buf_start + disk_buf_len; + + /* sync reader to this segment of the stream */ + p=disk_buf_start; + if (sync_data_stream(&p)) + { + DEBUGF("Could not sync stream\n"); + return PLUGIN_ERROR; + } + + /* find PTS >= start_time */ + audio_sync_start = 0; + audio_sync_time = 0; + video_sync_start = 0; + { + Stream tmp; + initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); + int cont_seek_loop = 1; + int coarse_seek = 1; + do + { + if ( accept_button ) + { + rb->yield(); + if (rb->button_available()) + return -101; + } + + while ( get_next_data(&tmp, 1) == 1 ) + { + if ( tmp.curr_packet_end == disk_buf_start ) + seek_pos += disk_buf_tail - disk_buf_start; + else + seek_pos += tmp.curr_packet_end - disk_buf_start; + if ((size_t)seek_pos > rb->filesize(in_file) - read_length) + seek_pos = rb->filesize(in_file) - read_length; + rb->lseek( in_file, seek_pos, SEEK_SET ); + disk_buf_len = rb->read ( in_file, disk_buf_start, read_length ); + disk_buf_tail = disk_buf_start + disk_buf_len; + + /* sync reader to this segment of the stream */ + p=disk_buf_start; + initialize_stream(&tmp,p,disk_buf_len,0xc0); + } + + /* are we after start_time in the stream? */ + if ( coarse_seek && (int)(tmp.curr_pts/45000) >= start_time*60 ) + { + int time_to_backup = (int)(tmp.curr_pts/45000) - start_time*60; + if (time_to_backup == 0) + time_to_backup++; + seek_pos -= seek_pos_sec_inc * time_to_backup; + seek_pos_sec_inc -= seek_pos_sec_inc/20; /* for stability */ + if (seek_pos<0) + seek_pos=0; + if ((size_t)seek_pos > rb->filesize(in_file) - read_length) + seek_pos = rb->filesize(in_file) - read_length; + rb->lseek( in_file, seek_pos, SEEK_SET ); + disk_buf_len = rb->read( in_file, disk_buf_start, read_length ); + disk_buf_tail = disk_buf_start + disk_buf_len; + + /* sync reader to this segment of the stream */ + p=disk_buf_start; + if (sync_data_stream(&p)) + { + DEBUGF("Could not sync stream\n"); + return PLUGIN_ERROR; + } + initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); + continue; + } + + /* are we well before start_time in the stream? */ + if ( coarse_seek && start_time*60 - (int)(tmp.curr_pts/45000) > 2 ) + { + int time_to_advance = start_time*60 - (int)(tmp.curr_pts/45000) - 2; + if (time_to_advance <= 0) + time_to_advance = 1; + seek_pos += seek_pos_sec_inc * time_to_advance; + if (seek_pos<0) + seek_pos=0; + if ((size_t)seek_pos > rb->filesize(in_file) - read_length) + seek_pos = rb->filesize(in_file) - read_length; + rb->lseek( in_file, seek_pos, SEEK_SET ); + disk_buf_len = rb->read ( in_file, disk_buf_start, read_length ); + disk_buf_tail = disk_buf_start + disk_buf_len; + + /* sync reader to this segment of the stream */ + p=disk_buf_start; + if (sync_data_stream(&p)) + { + DEBUGF("Could not sync stream\n"); + return PLUGIN_ERROR; + } + initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); + continue; + } + + coarse_seek = 0; + + /* are we at start_time in the stream? */ + if ( (int)(tmp.curr_pts/45000) >= start_time*60 ) + cont_seek_loop = 0; + + } + while ( cont_seek_loop ); + + + DEBUGF("start diff: %u %u\n",(unsigned int)(tmp.curr_pts/45000),start_time*60); + seek_pos+=tmp.curr_packet_end-disk_buf_start; + + last_seek_pos = seek_pos; + last_start_time = start_time; + + rb->lseek(in_file,seek_pos,SEEK_SET); + } + } + else + { + seek_pos = 0; + rb->lseek(in_file,0,SEEK_SET); + last_seek_pos = seek_pos; + last_start_time = start_time; + } + return seek_pos; } enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { int status = PLUGIN_ERROR; /* assume failure */ + int start_time=-1; void* audiobuf; ssize_t audiosize; int in_file; - uint8_t* buffer; - size_t file_remaining; size_t disk_buf_len; + ssize_t seek_pos; #ifndef HAVE_LCD_COLOR long graysize; int grayscales; #endif + audio_sync_start = 0; + audio_sync_time = 0; + video_sync_start = 0; + if (parameter == NULL) { api->splash(HZ*2, "No File"); @@ -1781,7 +2136,9 @@ PLUGIN_IRAM_INIT(api) rb = api; + rb->splash(0, "loading ..."); + /* sets audiosize and returns buffer pointer */ audiobuf = rb->plugin_get_audio_buffer(&audiosize); #if INPUT_SRC_CAPS != 0 @@ -1792,47 +2149,36 @@ rb->pcm_set_frequency(SAMPR_44); - /* Set disk pointers to NULL */ - disk_buf_end = disk_buf = NULL; +#ifndef HAVE_LCD_COLOR + /* initialize the grayscale buffer: 32 bitplanes for 33 shades of gray. */ + grayscales = gray_init(rb, audiobuf, audiosize, false, LCD_WIDTH, LCD_HEIGHT, + 32, 2<<8, &graysize) + 1; + audiobuf += graysize; + audiosize -= graysize; + if (grayscales < 33 || audiosize <= 0) + { + rb->splash(HZ, "gray buf error"); + return PLUGIN_ERROR; + } +#endif - /* Stream construction */ - /* We take the first stream of each (audio and video) */ - /* TODO : Search for these in the file first */ - audio_str.curr_packet_end = audio_str.curr_packet = audio_str.next_packet = NULL; - video_str = audio_str; - video_str.id = 0xe0; - audio_str.id = 0xc0; - /* Initialise our malloc buffer */ mpeg2_alloc_init(audiobuf,audiosize); + /* Set disk pointers to NULL */ + disk_buf_end = disk_buf_start = NULL; + /* Grab most of the buffer for the compressed video - leave some for PCM audio data and some for libmpeg2 malloc use. */ - buffer_size = audiosize - (PCMBUFFER_SIZE+PCMBUFFER_GUARD_SIZE+ + disk_buf_size = audiosize - (PCMBUFFER_SIZE+PCMBUFFER_GUARD_SIZE+ MPABUF_SIZE+LIBMPEG2BUFFER_SIZE); - DEBUGF("audiosize=%ld, buffer_size=%ld\n",audiosize,buffer_size); - buffer = mpeg2_malloc(buffer_size,-1); + DEBUGF("audiosize=%ld, disk_buf_size=%ld\n",audiosize,disk_buf_size); + disk_buf_start = mpeg2_malloc(disk_buf_size,-1); - if (buffer == NULL) + if (disk_buf_start == NULL) return PLUGIN_ERROR; -#ifndef HAVE_LCD_COLOR - /* initialize the grayscale buffer: 32 bitplanes for 33 shades of gray. */ - grayscales = gray_init(rb, buffer, buffer_size, false, LCD_WIDTH, LCD_HEIGHT, - 32, 2<<8, &graysize) + 1; - buffer += graysize; - buffer_size -= graysize; - if (grayscales < 33 || buffer_size <= 0) - { - rb->splash(HZ, "gray buf error"); - return PLUGIN_ERROR; - } -#endif - - buffer_size &= ~(0x7ff); /* Round buffer down to nearest 2KB */ - DEBUGF("audiosize=%ld, buffer_size=%ld\n",audiosize,buffer_size); - if (!init_mpabuf()) return PLUGIN_ERROR; @@ -1845,9 +2191,10 @@ in_file = rb->open((char*)parameter,O_RDONLY); if (in_file < 0){ - //fprintf(stderr,"Could not open %s\n",argv[1]); + DEBUGF("Could not open %s\n",(char*)parameter); return PLUGIN_ERROR; } + filename = (char*)parameter; #ifdef HAVE_LCD_COLOR rb->lcd_set_backdrop(NULL); @@ -1867,36 +2214,51 @@ /* From this point on we've altered settings, colors, cpu_boost, etc. and cannot just return PLUGIN_ERROR - instead drop though to cleanup code */ + init_settings((char*)parameter); - init_settings(); - - /* Msg queue init - no need for queue_remove since it's not a registered - queue */ - rb->queue_init( &msg_queue, false ); - /* Initialise libmad */ rb->memset(mad_frame_overlap, 0, sizeof(mad_frame_overlap)); init_mad(mad_frame_overlap); file_remaining = rb->filesize(in_file); - disk_buf_end = buffer + buffer_size-MPEG_GUARDBUF_SIZE; + disk_buf_end = disk_buf_start + disk_buf_size-MPEG_GUARDBUF_SIZE; + /* initalize play_time with the length (in minutes) of the movie + zero if the time could not be determined */ + find_length( in_file ); + + /* start menu */ + start_time = mpeg_start_menu(play_time, in_file); + if ( start_time == -1 ) + return 0; + else if ( start_time < 0 ) + start_time = 0; + else if ( start_time > play_time ) + start_time = play_time; + + rb->splash(0, "loading ..."); + + /* seek start time */ + seek_pos = seek_PTS( in_file, start_time, 0 ); + + rb->lseek(in_file,seek_pos,SEEK_SET); + video_thumb_print = 0; + audio_sync_start = 0; + audio_sync_time = 0; + video_sync_start = 0; + /* Read some stream data */ - disk_buf_len = rb->read (in_file, buffer, MPEG_LOW_WATERMARK); + disk_buf_len = rb->read (in_file, disk_buf_start, disk_buf_size - MPEG_GUARDBUF_SIZE); - DEBUGF("Initial Buffering - %d bytes\n",(int)disk_buf_len); - disk_buf = buffer; - disk_buf_tail = buffer+disk_buf_len; - file_remaining -= disk_buf_len; + disk_buf_tail = disk_buf_start + disk_buf_len; + file_remaining -= disk_buf_len + seek_pos; - video_str.guard_bytes = audio_str.guard_bytes = 0; - video_str.prev_packet = disk_buf; - audio_str.prev_packet = disk_buf; - video_str.buffer_remaining = disk_buf_len; - audio_str.buffer_remaining = disk_buf_len; + initialize_stream( &video_str, disk_buf_start, disk_buf_len, 0xe0 ); + initialize_stream( &audio_str, disk_buf_start, disk_buf_len, 0xc0 ); rb->spinlock_init(&audio_str.msg_lock); rb->spinlock_init(&video_str.msg_lock); + audio_str.status = STREAM_BUFFERING; video_str.status = STREAM_PLAYING; @@ -1932,17 +2294,29 @@ size_t audio_remaining = audio_str.buffer_remaining; size_t video_remaining = video_str.buffer_remaining; - if (MIN(audio_remaining,video_remaining) < MPEG_LOW_WATERMARK) { + if (MIN(audio_remaining,video_remaining) < MPEG_LOW_WATERMARK) + { - size_t bytes_to_read = buffer_size - MPEG_GUARDBUF_SIZE - + size_t bytes_to_read = disk_buf_size - MPEG_GUARDBUF_SIZE - MAX(audio_remaining,video_remaining); bytes_to_read = MIN(bytes_to_read,(size_t)(disk_buf_end-disk_buf_tail)); while (( bytes_to_read > 0) && (file_remaining > 0) && - ((audio_str.status >= 0) || (video_str.status >= 0))) { - size_t n = rb->read(in_file, disk_buf_tail, MIN(32*1024,bytes_to_read)); + ((audio_str.status != STREAM_DONE) || (video_str.status != STREAM_DONE))) + { + size_t n; + if ( video_sync_start != 0 ) + n = rb->read(in_file, disk_buf_tail, MIN(32*1024,bytes_to_read)); + else + { + n = rb->read(in_file, disk_buf_tail,bytes_to_read); + if (n==0) + rb->splash(30,"buffer fill error"); + } + + bytes_to_read -= n; file_remaining -= n; @@ -1957,7 +2331,7 @@ } if (disk_buf_tail == disk_buf_end) - disk_buf_tail = buffer; + disk_buf_tail = disk_buf_start; } rb->sleep(HZ/10); @@ -1976,6 +2350,8 @@ rb->sleep(HZ/10); + vo_cleanup(); + #ifndef HAVE_LCD_COLOR gray_release(); #endif @@ -1983,8 +2359,6 @@ rb->lcd_clear_display(); rb->lcd_update(); - mpeg2_close (mpeg2dec); - rb->close (in_file); #ifdef HAVE_ADJUSTABLE_CPU_FREQ Index: apps/plugins/mpegplayer/mpeg_settings.c =================================================================== --- apps/plugins/mpegplayer/mpeg_settings.c (revision 14627) +++ apps/plugins/mpegplayer/mpeg_settings.c (working copy) @@ -7,39 +7,344 @@ extern struct plugin_api* rb; struct mpeg_settings settings; -static struct mpeg_settings old_settings; +ssize_t seek_PTS(int in_file, int startTime, int accept_button); +void display_thumb(int in_file); + #define SETTINGS_VERSION 1 #define SETTINGS_MIN_VERSION 1 #define SETTINGS_FILENAME "mpegplayer.cfg" -static char* showfps_options[] = {"No", "Yes"}; -static char* limitfps_options[] = {"No", "Yes"}; -static char* skipframes_options[] = {"No", "Yes"}; +const unsigned char rockbox91x32[] = { + 0x00, 0x02, 0x7f, 0x02, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf8, 0xf8, 0xf0, + 0xe0, 0x80, 0x00, 0x00, 0x80, 0xe0, 0xf0, 0xf0, 0xf8, 0xfa, 0xfa, 0xf4, 0xf4, + 0xe8, 0x90, 0x60, 0x80, 0x60, 0x90, 0xe8, 0xf4, 0xf4, 0xfa, 0xfa, 0xfa, 0xfa, + 0xf4, 0xe8, 0x04, 0xf4, 0xf4, 0xf4, 0x04, 0xfe, 0x04, 0xc0, 0xf0, 0xf4, 0xf4, + 0x34, 0x1e, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x88, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0xc2, 0x3c, 0xc3, 0xff, + 0xff, 0xff, 0x00, 0xfe, 0xff, 0xff, 0x07, 0x01, 0x00, 0xfe, 0x02, 0xfd, 0x07, + 0xff, 0xff, 0xfe, 0x01, 0xfe, 0xff, 0xff, 0x0f, 0x01, 0x00, 0x80, 0x00, 0x01, + 0x03, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xc0, 0xf9, 0xfe, 0xff, 0xff, 0x07, 0x04, + 0x04, 0x04, 0xfc, 0x80, 0x40, 0x40, 0x40, 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x40, 0x40, + 0x40, 0x40, 0x80, 0x00, 0x80, 0x40, 0x80, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x00, + + 0x00, 0x7c, 0xc3, 0x9e, 0x67, 0x37, 0x8f, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, + 0xe3, 0x80, 0x00, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x01, 0x03, 0xc1, 0x3f, 0xc1, + 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0xc1, 0x01, 0x01, 0xff, 0x01, 0x00, + 0x00, 0xc0, 0x00, 0xff, 0xff, 0xff, 0xcf, 0x3f, 0xff, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x10, 0x08, 0x18, 0xe8, 0x10, 0xe0, 0x00, 0x01, 0x02, 0x04, + 0x02, 0x01, 0xe0, 0x10, 0x08, 0x08, 0x18, 0xe8, 0x10, 0xe0, 0x00, 0x01, 0x00, + 0x00, 0xe0, 0x40, 0x01, 0x02, 0x01, 0x40, 0xe0, 0x18, 0xe4, 0x1b, 0x04, 0x03, + + 0x00, 0x00, 0x00, 0x00, 0x77, 0x6e, 0x61, 0x7f, 0x40, 0xff, 0x00, 0x07, 0x1f, + 0x3f, 0x7f, 0x7c, 0x71, 0xcf, 0x5f, 0x3f, 0x7e, 0x7c, 0x7d, 0x7d, 0x7e, 0x3f, + 0x1f, 0x0f, 0x01, 0x00, 0x41, 0x47, 0x5f, 0x7f, 0x7e, 0x7c, 0x7f, 0x7c, 0x7e, + 0x3f, 0x1f, 0x00, 0x7f, 0x7f, 0x7f, 0x01, 0x4e, 0x71, 0xcf, 0x7f, 0x40, 0x40, + 0x40, 0x40, 0x60, 0x21, 0x42, 0x43, 0x42, 0x41, 0x40, 0x20, 0x10, 0x08, 0x04, + 0x08, 0x10, 0x20, 0x41, 0x42, 0x42, 0x43, 0x42, 0x41, 0x40, 0x60, 0x50, 0x40, + 0x40, 0x40, 0x20, 0x50, 0x28, 0x10, 0x20, 0x40, 0x43, 0x44, 0x5b, 0x64, 0x18, +}; + +#define LOGO rockbox91x32 +#define LOGO_WIDTH 91 +#define LOGO_HEIGHT 32 + +enum sliderState_t {state0, state1, state2, state3, state4, state5} sliderState; +volatile long thumbDelayTimer; + +/* button definitions */ +#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD) +#define MPEG_SELECT BUTTON_PLAY +#define MPEG_RIGHT BUTTON_RIGHT +#define MPEG_LEFT BUTTON_LEFT +#define MPEG_SCROLL_DOWN BUTTON_SCROLL_UP +#define MPEG_SCROLL_UP BUTTON_SCROLL_DOWN +#define MPEG_EXIT BUTTON_POWER + +#elif (CONFIG_KEYPAD == IPOD_3G_PAD) || (CONFIG_KEYPAD == IPOD_4G_PAD) +#define MPEG_SELECT BUTTON_SELECT +#define MPEG_RIGHT BUTTON_RIGHT +#define MPEG_LEFT BUTTON_LEFT +#define MPEG_SCROLL_DOWN BUTTON_SCROLL_BACK +#define MPEG_SCROLL_UP BUTTON_SCROLL_FWD +#define MPEG_EXIT BUTTON_MENU + +#elif CONFIG_KEYPAD == GIGABEAT_PAD +#define MPEG_SELECT BUTTON_SELECT +#define MPEG_LEFT BUTTON_LEFT +#define MPEG_RIGHT BUTTON_RIGHT +#define MPEG_SCROLL_DOWN BUTTON_VOL_DOWN +#define MPEG_SCROLL_UP BUTTON_VOL_UP +#define MPEG_EXIT BUTTON_POWER + +#elif CONFIG_KEYPAD == IRIVER_H10_PAD +#define MPEG_SELECT BUTTON_PLAY +#define MPEG_SCROLL_UP BUTTON_SCROLL_UP +#define MPEG_SCROLL_DOWN BUTTON_SCROLL_DOWN +#define MPEG_LEFT BUTTON_LEFT +#define MPEG_RIGHT BUTTON_RIGHT +#define MPEG_EXIT BUTTON_POWER + +#elif CONFIG_KEYPAD == SANSA_E200_PAD +#define MPEG_SELECT BUTTON_SELECT +#define MPEG_SCROLL_UP BUTTON_SCROLL_UP +#define MPEG_SCROLL_DOWN BUTTON_SCROLL_DOWN +#define MPEG_LEFT BUTTON_LEFT +#define MPEG_RIGHT BUTTON_RIGHT +#define MPEG_EXIT BUTTON_POWER + +#else +#error MPEGPLAYER: Unsupported keypad +#endif + static struct configdata config[] = { - {TYPE_ENUM, 0, 2, &settings.showfps, "Show FPS", showfps_options, NULL}, - {TYPE_ENUM, 0, 2, &settings.limitfps, "Limit FPS", limitfps_options, NULL}, - {TYPE_ENUM, 0, 2, &settings.skipframes, "Skip frames", skipframes_options, NULL}, + {TYPE_INT, 0, 2, &settings.showfps, "Show FPS", NULL, NULL}, + {TYPE_INT, 0, 2, &settings.limitfps, "Limit FPS", NULL, NULL}, + {TYPE_INT, 0, 2, &settings.skipframes, "Skip frames", NULL, NULL}, }; +void draw_logo(int x, int y, int w, int h) +{ + rb->lcd_mono_bitmap(LOGO, x+((w-LOGO_WIDTH)/2), y+((h-LOGO_HEIGHT)/2), + LOGO_WIDTH, LOGO_HEIGHT); + rb->lcd_drawrect(x, y, w, h); + rb->lcd_update_rect(x, y, w, h); +} + +void draw_slider(int slider_ypos, int max_val, int current_val) +{ + float slider_margin = LCD_WIDTH*0.1; + float slider_width = LCD_WIDTH-(2*slider_margin); + float fill_rate = slider_width/max_val; + char resume_str[32]; + + rb->snprintf(resume_str, sizeof(resume_str), "%u", 0); + rb->lcd_putsxy(slider_margin, slider_ypos, resume_str); + + rb->snprintf(resume_str, sizeof(resume_str), "%u", max_val); + rb->lcd_putsxy(LCD_WIDTH-slider_margin-16, slider_ypos, resume_str); + + rb->lcd_drawrect(slider_margin, slider_ypos+17, slider_width, 8); + rb->lcd_fillrect(slider_margin, slider_ypos+17, + fill_rate*current_val, 8); + + rb->snprintf(resume_str, sizeof(resume_str), "%u", current_val); + rb->lcd_putsxy(slider_margin+(fill_rate*current_val-5), + slider_ypos+29, resume_str); + + rb->lcd_update_rect(0, slider_ypos, LCD_WIDTH, LCD_HEIGHT-slider_ypos); +} + +int get_start_time(int play_time, int in_file) +{ + int quit = 0; + int button = 0; + int resume_time = settings.resume_time; + + int preview_height = 90; + int preview_width = 114; + int preview_ypos = (LCD_HEIGHT/2)-(preview_height/2); + int preview_xpos = (LCD_WIDTH/2)-(preview_width/2); + + int seek_rtn; + + sliderState = state0; + thumbDelayTimer = *(rb->current_tick); + draw_logo(preview_xpos, preview_ypos, preview_width, preview_height); + + while(quit == 0) + { + button = rb->button_get(false); + switch (button) + { + case MPEG_SCROLL_UP: + case MPEG_SCROLL_UP | BUTTON_REPEAT: + case MPEG_LEFT: + case MPEG_LEFT | BUTTON_REPEAT: + if (--resume_time < 0) + resume_time = 0; + sliderState = state0; + thumbDelayTimer = *(rb->current_tick); + break; + case MPEG_SCROLL_DOWN: + case MPEG_SCROLL_DOWN | BUTTON_REPEAT: + case MPEG_RIGHT: + case MPEG_RIGHT | BUTTON_REPEAT: + if (++resume_time > play_time) + resume_time = play_time; + sliderState = state0; + thumbDelayTimer = *(rb->current_tick); + break; + case MPEG_SELECT: + quit = 1; + break; + case MPEG_EXIT: + resume_time = -1; + quit = 1; + break; + default: + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) + { + resume_time = -1; + quit = 1; + } + break; + } + + rb->yield(); + + if (sliderState == state0) + { + rb->lcd_clear_display(); + rb->splash(0, "loading ..."); + sliderState = state1; + } + + else if (sliderState == state1) + { + if (*(rb->current_tick) - thumbDelayTimer > 75) + sliderState = state2; + if (resume_time == 0) + { + seek_rtn = 0; + draw_logo(preview_xpos, preview_ypos, preview_width, preview_height); + sliderState = state5; + } + draw_slider(preview_ypos+preview_height+20, play_time, resume_time); + } + + else if (sliderState == 2) + { + if ( (seek_rtn = seek_PTS(in_file, resume_time, 1)) >= 0) + sliderState = state3; + else if ( seek_rtn == -101) + { + sliderState = 0; + thumbDelayTimer = *(rb->current_tick); + } + else + { + draw_logo(preview_xpos, preview_ypos, preview_width, preview_height); + sliderState = state4; + } + } + + else if (sliderState == 3) + { + display_thumb(in_file); + sliderState = 4; + } + + else if (sliderState == 4) + { + draw_slider(preview_ypos+preview_height+20, play_time, resume_time); + sliderState = 5; + } + } + + return resume_time; +} + +int mpeg_start_menu(int play_time, int in_file) +{ + int m; + int result = 0; + int menu_quit = 0; + + // add the resume time to the menu display + char resume_str[32]; + rb->snprintf(resume_str, sizeof(resume_str), + "Resume time (min): %u", settings.resume_time); + + struct menu_item items[] = { + { "Play from beginning", NULL }, + { resume_str, NULL }, + { "Set start time (min)", NULL }, + { "Quit mpegplayer", NULL }, + }; + + m = menu_init(rb, items, sizeof(items) / sizeof(*items), + NULL, NULL, NULL, NULL); + + rb->button_clear_queue(); + + while(menu_quit == 0) + { + result = menu_show(m); + + switch (result) + { + case 0: + menu_quit = 1; + result = 0; + break; + case 1: + menu_quit = 1; + result = settings.resume_time; + break; + case 2: + if ((result = get_start_time(play_time, in_file)) >= 0) + menu_quit = 1; + break; + case 3: + menu_quit = 1; + result = -1; + break; + default: + if (result == MENU_ATTACHED_USB) + { + menu_quit = 1; + result = -1; + } + break; + } + } + menu_exit(m); + + settings.resume_time = result; + return (int)result; +} + +void clear_resume_count(void); +void clear_resume_count() +{ + configfile_save(SETTINGS_FILENAME, config, + sizeof(config)/sizeof(*config), + SETTINGS_VERSION); + + settings.resume_count = 0; + + /* add this place holder so the count is above resume entries */ + configfile_update_entry(SETTINGS_FILENAME, "Resume count", 0); +} + bool mpeg_menu(void) { int m; int result; int menu_quit=0; + // add the clear resume option to the menu display + char clear_str[32]; + rb->snprintf(clear_str, sizeof(clear_str), + "Clear all resumes: %u", settings.resume_count); + static const struct opt_items noyes[2] = { { "No", -1 }, - { "Yes", -1 }, + { "Yes", -1 }, }; - static const struct menu_item items[] = { + struct menu_item items[] = { { "Display FPS", NULL }, { "Limit FPS", NULL }, { "Skip frames", NULL }, - { "Quit mpegplayer", NULL }, + { clear_str, NULL }, + { "Quit mpegplayer", NULL } }; m = menu_init(rb, items, sizeof(items) / sizeof(*items), @@ -64,6 +369,11 @@ rb->set_option("Skip frames",&settings.skipframes,INT, noyes, 2, NULL); break; + case 3: /* Clear resumes */ + clear_resume_count(); + rb->snprintf(clear_str, sizeof(clear_str), + "Clear all resumes: %u", 0); + break; default: menu_quit=1; if (result == MENU_ATTACHED_USB) @@ -77,45 +387,65 @@ rb->lcd_clear_display(); rb->lcd_update(); - return (result==3); + return (result==4); } -void init_settings(void) +void init_settings(const char* filename) { /* Set the default settings */ settings.showfps = 0; /* Do not show FPS */ settings.limitfps = 1; /* Limit FPS */ settings.skipframes = 1; /* Skip frames */ + settings.resume_count = -1; configfile_init(rb); - if (configfile_load(SETTINGS_FILENAME, config, - sizeof(config)/sizeof(*config), - SETTINGS_MIN_VERSION - ) < 0) + /* If the config file don't contain resume count + or the load fails, then rebuild the config file. + This eliminates the worry for older config files + having unused data. */ + if (((settings.resume_count = configfile_get_value + (SETTINGS_FILENAME, "Resume count")) < 0) || + (configfile_load(SETTINGS_FILENAME, config, + sizeof(config)/sizeof(*config), + SETTINGS_MIN_VERSION) < 0)) { - /* If the loading failed, save a new config file (as the disk is - already spinning) */ + /* Generate a new config file with default values */ configfile_save(SETTINGS_FILENAME, config, sizeof(config)/sizeof(*config), SETTINGS_VERSION); } - /* Keep a copy of the saved version of the settings - so we can check if - the settings have changed when we quit */ - old_settings = settings; + if (settings.resume_count < 0) + { + settings.resume_count = 0; + + /* add this place holder so the count is above resume entries */ + configfile_update_entry(SETTINGS_FILENAME, "Resume count", 0); + } + + rb->strcpy(settings.resume_filename, filename); + + /* get the resume time for the current mpeg if it exist */ + if ((settings.resume_time = configfile_get_value + (SETTINGS_FILENAME, filename)) < 0) + { + settings.resume_time = 0; + } } void save_settings(void) { - /* Save the user settings if they have changed */ - if (rb->memcmp(&settings,&old_settings,sizeof(settings))!=0) { - configfile_save(SETTINGS_FILENAME, config, - sizeof(config)/sizeof(*config), - SETTINGS_VERSION); + configfile_update_entry(SETTINGS_FILENAME, "Show FPS", settings.showfps); + configfile_update_entry(SETTINGS_FILENAME, "Limit FPS", settings.limitfps); + configfile_update_entry(SETTINGS_FILENAME, "Skip frames", settings.skipframes); - /* Store the settings in old_settings - to check for future changes */ - old_settings = settings; - } + /* If this was a new resume entry then update the total resume count */ + if (configfile_update_entry(SETTINGS_FILENAME, settings.resume_filename, + settings.resume_time) == 0) + { + configfile_update_entry(SETTINGS_FILENAME, "Resume count", + ++settings.resume_count); + } } Index: apps/plugins/mpegplayer/mpeg_settings.h =================================================================== --- apps/plugins/mpegplayer/mpeg_settings.h (revision 14627) +++ apps/plugins/mpegplayer/mpeg_settings.h (working copy) @@ -1,14 +1,20 @@ #include "plugin.h" -struct mpeg_settings { - int showfps; - int limitfps; - int skipframes; +struct mpeg_settings +{ + int showfps; /* flag to display fps */ + int limitfps; /* flag to limit fps */ + int skipframes; /* flag to skip frames */ + int resume_count; /* total # of resumes in config file */ + int resume_time; /* resume time for current mpeg */ + char resume_filename[128]; /* filename of current mpeg */ }; extern struct mpeg_settings settings; +int get_start_time(int play_time, int in_file); +int mpeg_start_menu(int play_time, int in_file); bool mpeg_menu(void); -void init_settings(void); +void init_settings(const char* filename); void save_settings(void); Index: apps/plugins/mpegplayer/idct.c =================================================================== --- apps/plugins/mpegplayer/idct.c (revision 14627) +++ apps/plugins/mpegplayer/idct.c (working copy) @@ -260,6 +260,8 @@ void mpeg2_idct_init (void) { + extern uint8_t default_mpeg2_scan_norm[64]; + extern uint8_t default_mpeg2_scan_alt[64]; extern uint8_t mpeg2_scan_norm[64]; extern uint8_t mpeg2_scan_alt[64]; int i, j; @@ -274,10 +276,10 @@ for (i = 0; i < 64; i++) { - j = mpeg2_scan_norm[i]; + j = default_mpeg2_scan_norm[i]; mpeg2_scan_norm[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2); - j = mpeg2_scan_alt[i]; + j = default_mpeg2_scan_alt[i]; mpeg2_scan_alt[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2); } } Index: apps/plugins/mpegplayer/video_out_rockbox.c =================================================================== --- apps/plugins/mpegplayer/video_out_rockbox.c (revision 14627) +++ apps/plugins/mpegplayer/video_out_rockbox.c (working copy) @@ -264,10 +264,111 @@ #define SCREEN_HEIGHT LCD_WIDTH #endif +uint8_t* tmpbufa = 0; +uint8_t* tmpbufb = 0; +uint8_t* tmpbufc = 0; +uint8_t* tmpbuf[3]; + +void vo_draw_frame_thumb (uint8_t * const * buf) +{ + +/* DEBUGF("screen (w,h) (%u,%i)\n",SCREEN_WIDTH,SCREEN_HEIGHT); */ +/* DEBUGF("image (w,h) (%u,%i)\n",image_width,image_height); */ + + int r,c; + + /* image_width is 224 (224x176)*/ + +#if LCD_WIDTH >= LCD_HEIGHT + for (r=0;r= LCD_HEIGHT + yuv_bitmap_part(tmpbuf,0,0,image_width/2, + (LCD_WIDTH-1-image_width/2)/2, + (LCD_HEIGHT-1-image_height/2)/2, + output_width/2,output_height/2); + + rb->lcd_update_rect(output_x,output_y,output_width,output_height); +#else + yuv_bitmap_part(tmpbuf,0,0,image_height/2, + (LCD_HEIGHT-1-image_height/2)/2, + (LCD_WIDTH-1-image_width/2)/2, + output_height/2,output_width/2); + + rb->lcd_update_rect(output_y,output_x,output_height,output_width); +#endif +#else +#if LCD_WIDTH >= LCD_HEIGHT + rb->lcd_yuv_blit(tmpbuf,0,0,image_width/2, + (LCD_WIDTH-1-image_width/2)/2, + (LCD_HEIGHT-1-image_height/2)/2, + output_width/2,output_height/2); +#else + rb->lcd_yuv_blit(tmpbuf,0,0,image_height/2, + (LCD_HEIGHT-1-image_height/2)/2, + (LCD_WIDTH-1-image_width/2)/2, + output_height/2,output_width/2); +#endif +#endif +#else +#if LCD_WIDTH >= LCD_HEIGHT + gray_ub_gray_bitmap_part(tmpbuf[0],0,0,image_width/2, + (LCD_WIDTH-1-image_width/2)/2, + (LCD_HEIGHT-1-image_height/2)/2, + output_width/2,output_height/2); +#else + gray_ub_gray_bitmap_part(tmpbuf[0],0,0,image_height/2, + (LCD_HEIGHT-1-image_height/2)/2, + (LCD_WIDTH-1-image_width/2)/2, + output_height/2,output_width/2); +#endif +#endif +} + void vo_setup(const mpeg2_sequence_t * sequence) { image_width=sequence->width; image_height=sequence->height; + + tmpbufa = (uint8_t*)mpeg2_malloc(sizeof(uint8_t)*image_width/2* + image_height/2, -2); + tmpbufb = (uint8_t*)mpeg2_malloc(sizeof(uint8_t)*image_width/4* + image_height/4, -2); + tmpbufc = (uint8_t*)mpeg2_malloc(sizeof(uint8_t)*image_width/4* + image_height/4, -2); + tmpbuf[0] = tmpbufa; + tmpbuf[1] = tmpbufb; + tmpbuf[2] = tmpbufc; + image_chroma_x=image_width/sequence->chroma_width; image_chroma_y=image_height/sequence->chroma_height; @@ -287,3 +388,13 @@ output_y = (SCREEN_HEIGHT-sequence->display_height)/2; } } + +void vo_cleanup() +{ + if (tmpbufc) + mpeg2_free(tmpbufc); + if (tmpbufb) + mpeg2_free(tmpbufb); + if (tmpbufa) + mpeg2_free(tmpbufa); +} Index: apps/plugins/mpegplayer/header.c =================================================================== --- apps/plugins/mpegplayer/header.c (revision 14627) +++ apps/plugins/mpegplayer/header.c (working copy) @@ -58,7 +58,7 @@ 83 }; -uint8_t mpeg2_scan_norm[64] IDATA_ATTR = { +uint8_t default_mpeg2_scan_norm[64] IDATA_ATTR = { /* Zig-Zag scan pattern */ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, @@ -66,7 +66,7 @@ 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; -uint8_t mpeg2_scan_alt[64] IDATA_ATTR = { +uint8_t default_mpeg2_scan_alt[64] IDATA_ATTR = { /* Alternate scan pattern */ 0, 8, 16, 24, 1, 9, 2, 10, 17, 25, 32, 40, 48, 56, 57, 49, 41, 33, 26, 18, 3, 11, 4, 12, 19, 27, 34, 42, 50, 58, 35, 43, @@ -74,6 +74,9 @@ 53, 61, 22, 30, 7, 15, 23, 31, 38, 46, 54, 62, 39, 47, 55, 63 }; +uint8_t mpeg2_scan_norm[64] IDATA_ATTR; +uint8_t mpeg2_scan_alt[64] IDATA_ATTR; + void mpeg2_header_state_init (mpeg2dec_t * mpeg2dec) { if (mpeg2dec->sequence.width != (unsigned)-1) { Index: apps/plugins/mpegplayer/alloc.c =================================================================== --- apps/plugins/mpegplayer/alloc.c (revision 14627) +++ apps/plugins/mpegplayer/alloc.c (working copy) @@ -47,7 +47,6 @@ (void)reason; - DEBUGF("mpeg2_malloc(%d,%d)\n",size,reason); if (mem_ptr + (long)size > bufsize) { DEBUGF("OUT OF MEMORY\n"); return NULL; @@ -56,11 +55,14 @@ x=&mallocbuf[mem_ptr]; mem_ptr+=(size+3)&~3; /* Keep memory 32-bit aligned */ + rb->memset(x,0,size); + + DEBUGF("mpeg2_malloc(%u,%d,%d)\n",(unsigned int)x,size,reason); return(x); } void mpeg2_free(void* ptr) { - (void)ptr; + mem_ptr = (void *)ptr - (void *)mallocbuf; } /* gcc may want to use memcpy before rb is initialised, so here's a trivial Index: apps/plugins/calendar.c =================================================================== --- apps/plugins/calendar.c (revision 14627) +++ apps/plugins/calendar.c (working copy) @@ -25,6 +25,15 @@ PLUGIN_HEADER +#if CONFIG_KEYPAD == SANSA_E200_PAD +#define UP BUTTON_UP +#define DOWN BUTTON_DOWN +#define LEFT BUTTON_LEFT +#define RIGHT BUTTON_RIGHT +#define BUTTON_PLAY BUTTON_SELECT +#define BUTTON_ON BUTTON_POWER +#endif + static struct plugin_api* rb; static bool leap_year; @@ -109,7 +118,7 @@ char *Dayname[7] = {"M","T","W","T","F","S","S"}; int ws = 2; rb->lcd_getstringsize("A",&w,&h); - for (i = 0; i < 8;) + for (i = 0; i < 7;) { rb->lcd_putsxy(ws, 0 , Dayname[i++]); ws += space; @@ -218,7 +227,7 @@ for (k = 0; k < 7; k++) wday_has_memo[k] = false; memos_in_memory = 0; - fp = rb->open("/.rockbox/.memo",O_RDONLY); + fp = rb->open("/.rockbox/rocks/apps/.memo",O_RDONLY); if (fp > -1) { int count = rb->filesize(fp); @@ -315,7 +324,7 @@ static bool save_memo(int changed, bool new_mod, struct shown *shown) { int fp,fq; - fp = rb->open("/.rockbox/.memo",O_RDONLY | O_CREAT); + fp = rb->open("/.rockbox/rocks/apps/.memo",O_RDONLY | O_CREAT); fq = rb->creat("/.rockbox/~temp"); if ( (fq != -1) && (fp != -1) ) { @@ -345,7 +354,7 @@ rb->write(fq,temp,1); } rb->close(fp); - fp = rb->creat("/.rockbox/.memo"); + fp = rb->creat("/.rockbox/rocks/apps/.memo"); rb->lseek(fp, 0, SEEK_SET); rb->lseek(fq, 0, SEEK_SET); for (i = 0; i < rb->filesize(fq); i++) @@ -420,27 +429,40 @@ if (memos_in_shown_memory > 0) { rb->lcd_puts(0,0,"Remove : Up"); - rb->lcd_puts(0,1,"Edit : Down"); + rb->lcd_puts(0,1,"Edit : Down"); rb->lcd_puts(0,2,"New :"); - rb->lcd_puts(2,3,"weekly : Left"); - rb->lcd_puts(2,4,"monthly : Play"); + rb->lcd_puts(2,3,"weekly : Left"); + rb->lcd_puts(2,4,"monthly : Play"); rb->lcd_puts(2,5,"annually : Right"); - rb->lcd_puts(2,6,"one off : On"); + rb->lcd_puts(2,6,"one off : On"); + #ifdef SANSA_E200 + rb->lcd_puts(2,7,"Return : Scroll Up"); + #endif + } else { rb->lcd_puts(0,0,"New :"); - rb->lcd_puts(2,1,"weekly : Left"); + rb->lcd_puts(2,1,"weekly : Left"); rb->lcd_puts(2,2,"monthly : Play"); - rb->lcd_puts(2,3,"anualy : Right"); + rb->lcd_puts(2,3,"anualy : Right"); rb->lcd_puts(2,4,"one off : On"); + #ifdef SANSA_E200 + rb->lcd_puts(2,5,"Return : Scroll Up"); + #endif } rb->lcd_update(); button = rb->button_get(true); switch (button) { + + #ifdef SANSA_E200 + case BUTTON_SCROLL_UP: + return false; + #else case BUTTON_OFF: return false; + #endif case BUTTON_LEFT: add_memo(shown,0); @@ -590,10 +612,13 @@ case BUTTON_PLAY: return edit_memo(lines_displayed, shown); - +#ifdef SANSA_E200 + case BUTTON_ON: + return false; +#else case BUTTON_OFF: return false; - +#endif default: if(rb->default_event_handler(button) == SYS_USB_CONNECTED) been_in_usb_mode = true; @@ -679,6 +704,33 @@ button = rb->button_get(true); switch (button) { + +#ifdef SANSA_E200 + + case BUTTON_ON: + return false; + + case BUTTON_DOWN: + case BUTTON_DOWN | BUTTON_REPEAT: + next_month(&shown, 0); + break; + + case BUTTON_UP: + case BUTTON_UP | BUTTON_REPEAT: + prev_month(&shown, 0); + break; + + case BUTTON_SCROLL_DOWN: + case BUTTON_SCROLL_DOWN | BUTTON_REPEAT: + next_day(&shown, 7); + break; + + case BUTTON_SCROLL_UP: + case BUTTON_SCROLL_UP | BUTTON_REPEAT: + prev_day(&shown, 7); + break; + +#else case BUTTON_OFF: return false; @@ -702,6 +754,8 @@ prev_day(&shown, 7); break; +#endif + case BUTTON_LEFT: case BUTTON_LEFT | BUTTON_REPEAT: prev_day(&shown, 1); Index: apps/plugins/lib/configfile.c =================================================================== --- apps/plugins/lib/configfile.c (revision 14627) +++ apps/plugins/lib/configfile.c (working copy) @@ -55,12 +55,14 @@ if(fd < 0) return fd*10 - 1; - cfg_rb->fdprintf(fd, "file version: %d\n", version); + /* pre-allocate 10 bytes for INT */ + cfg_rb->fdprintf(fd, "file version: %10d\n", version); for(i = 0;i < num_items;i++) { switch(cfg[i].type) { case TYPE_INT: - cfg_rb->fdprintf(fd, "%s: %d\n", + /* pre-allocate 10 bytes for INT */ + cfg_rb->fdprintf(fd, "%s: %10d\n", cfg[i].name, *cfg[i].val); break; @@ -141,3 +143,72 @@ cfg_rb->close(fd); return 0; } + +int configfile_get_value(const char* filename, const char* name) +{ + int fd; + char *pname; + char *pval; + char buf[MAX_PATH]; + + get_cfg_filename(buf, MAX_PATH, filename); + fd = cfg_rb->open(buf, O_RDONLY); + if(fd < 0) + return -1; + + while(cfg_rb->read_line(fd, buf, MAX_PATH) > 0) + { + cfg_rb->settings_parseline(buf, &pname, &pval); + if(!cfg_rb->strcmp(name, pname)) + { + cfg_rb->close(fd); + return cfg_rb->atoi(pval); + } + } + + cfg_rb->close(fd); + return -1; +} + +int configfile_update_entry(const char* filename, const char* name, int val) +{ + int fd; + char *pname; + char *pval; + char path[MAX_PATH]; + char buf[256]; + int found = 0; + int line_len = 0; + int pos = 0; + + /* open the current config file */ + get_cfg_filename(path, MAX_PATH, filename); + fd = cfg_rb->open(path, O_RDWR); + if(fd < 0) + return -1; + + /* read in the current stored settings */ + while((line_len = cfg_rb->read_line(fd, buf, 256)) > 0) + { + cfg_rb->settings_parseline(buf, &pname, &pval); + + if(!cfg_rb->strcmp(name, pname)) + { + found = 1; + cfg_rb->lseek(fd, pos, SEEK_SET); + /* pre-allocate 10 bytes for INT */ + cfg_rb->fdprintf(fd, "%s: %10u\n", pname, val); + break; + } + pos += line_len; + } + + /* if (name/val) is a new entry just append to file */ + if (found == 0) + /* pre-allocate 10 bytes for INT */ + cfg_rb->fdprintf(fd, "%s: %10u\n", name, val); + + cfg_rb->close(fd); + + return found; +} Index: apps/plugins/lib/configfile.h =================================================================== --- apps/plugins/lib/configfile.h (revision 14627) +++ apps/plugins/lib/configfile.h (working copy) @@ -38,9 +38,41 @@ }; void configfile_init(struct plugin_api* newrb); + +/* configfile_save - Given configdata entries this function will + create a config file with these entries, destroying any + previous config file of the same name */ int configfile_save(const char *filename, struct configdata *cfg, int num_items, int version); + int configfile_load(const char *filename, struct configdata *cfg, int num_items, int min_version); +/* configfile_get_value - Given a key name, this function will + return the integer value for that key. + + Input: + filename = config file filename + name = (name/value) pair name entry + Return: + value if (name/value) pair is found + -1 if entry is not found +*/ +int configfile_get_value(const char* filename, const char* name); + +/* configure_update_entry - Given a key name and integer value + this function will update the entry if found, or add it if + not found. + + Input: + filename = config file filename + name = (name/value) pair name entry + val = new value for (name/value) pair + Return: + 1 if the (name/value) pair was found and updated with the new value + 0 if the (name/value) pair was added as a new entry + -1 if error +*/ +int configfile_update_entry(const char* filename, const char* name, int val); + #endif Index: apps/plugins/lib/bmp.h =================================================================== --- apps/plugins/lib/bmp.h (revision 14627) +++ apps/plugins/lib/bmp.h (working copy) @@ -21,6 +21,7 @@ #include "lcd.h" #include "plugin.h" +#include "recorder/bmp.h" /** * Save bitmap to file Index: apps/plugins/jpeg.c =================================================================== --- apps/plugins/jpeg.c (revision 14627) +++ apps/plugins/jpeg.c (working copy) @@ -2449,6 +2449,7 @@ MIID_QUIT = 0, MIID_TOGGLE_SS_MODE, MIID_CHANGE_SS_MODE, + MIID_DELETE, #if PLUGIN_BUFFER_SIZE >= MIN_MEM MIID_SHOW_PLAYBACK_MENU, #endif @@ -2465,6 +2466,8 @@ { "Toggle Slideshow Mode", NULL }, [MIID_CHANGE_SS_MODE] = { "Change Slideshow Time", NULL }, + [MIID_DELETE]= + { "Delete Current File", NULL }, #if PLUGIN_BUFFER_SIZE >= MIN_MEM [MIID_SHOW_PLAYBACK_MENU] = { "Show Playback Menu", NULL }, @@ -2507,6 +2510,9 @@ menu_exit(m); return 1; break; + case MIID_DELETE: + return 2; + break; case MIID_TOGGLE_SS_MODE: rb->set_option("Toggle Slideshow", &slideshow_enabled, INT, slideshow , 2, NULL); @@ -2572,9 +2578,10 @@ return 0; } /* interactively scroll around the image */ -int scroll_bmp(struct t_disp* pdisp) +int scroll_bmp(struct t_disp* pdisp, char *filename) { int lastbutton = 0; + int menu_return = 0; while (true) { @@ -2767,9 +2774,38 @@ #ifdef USEGSLIB gray_show(false); /* switch off grayscale overlay */ #endif - if (show_menu() == 1) - return PLUGIN_OK; - + menu_return = show_menu(); + if (menu_return == 1) + { + return PLUGIN_OK; + } + if (menu_return == 2) + { + rb->splash(HZ*1, "Press RIGHT to delete: %s", filename); + rb->lcd_clear_display(); + rb->splash(HZ, "Any other KEY to continue"); + button = rb->button_get(true); + switch(button) + { + case JPEG_RIGHT: + rb->remove(filename); + entries--; + if (entries < 1){ + rb->splash(HZ, "No More Files."); + return 1; + break; + }else{ + return change_filename(DIR_NEXT); + break; + } + default: + rb->lcd_clear_display(); + rb->splash(HZ*1/2, "Did not delete: %s", filename); + return change_filename(DIR_NONE); + break; + } + break; + } #ifdef USEGSLIB gray_show(true); /* switch on grayscale overlay */ #else @@ -3010,7 +3046,12 @@ { rb->snprintf(print,sizeof(print),"err opening %s:%d",filename,fd); rb->splash(HZ, print); - return PLUGIN_ERROR; + if (entries < 0){ + rb->splash(HZ, "No Files."); + return PLUGIN_ERROR; + }else{ + return change_filename(DIR_PREV); + } } filesize = rb->filesize(fd); rb->memset(&disp, 0, sizeof(disp)); @@ -3204,7 +3245,7 @@ */ while (1) { - status = scroll_bmp(p_disp); + status = scroll_bmp(p_disp, filename); if (status == ZOOM_IN) { if (ds > ds_min) @@ -3243,6 +3284,7 @@ #ifdef USEGSLIB rb->lcd_update(); #endif + rb->set_current_file(filename); return status; } Index: apps/plugins/viewer.c =================================================================== --- apps/plugins/viewer.c (revision 14627) +++ apps/plugins/viewer.c (working copy) @@ -21,6 +21,7 @@ #include #include "playback_control.h" #include "oldmenuapi.h" +#include "helper.h" PLUGIN_HEADER @@ -1365,7 +1366,9 @@ viewer_reset_settings(); /* load defaults first */ viewer_load_settings(); /* .. then try to load from disk */ - + /* Turn off backlight timeout */ + backlight_force_on(rb); /* backlight control in lib/helper.c */ + viewer_draw(col); while (!done) { @@ -1516,6 +1519,8 @@ rb->yield(); } } + /* Turn on backlight timeout (revert to settings) */ + backlight_use_settings(rb); /* backlight control in lib/helper.c */ return PLUGIN_OK; } Index: apps/plugins/SOURCES =================================================================== --- apps/plugins/SOURCES (revision 14627) +++ apps/plugins/SOURCES (working copy) @@ -1,4 +1,9 @@ /* plugins common to all models */ +language_quiz.c +splash.c +Delete-Playlist.c +HTML_Parser.c +calendar.c battery_bench.c chessclock.c credits.c @@ -20,6 +25,10 @@ vbrfix.c viewer.c +#if defined(SANSA_E200) +Playlist_Converter.c +#endif + #if CONFIG_CODEC == SWCODEC metronome.c #endif @@ -56,6 +65,10 @@ invadrox.c #endif +#if (LCD_DEPTH >= 2) +tictactoe.c +#endif + #if LCD_WIDTH != 128 /* These need adjusting for the iRiver if'p screen */ brickmania.c Index: apps/plugins/SUBDIRS =================================================================== --- apps/plugins/SUBDIRS (revision 14627) +++ apps/plugins/SUBDIRS (working copy) @@ -41,6 +41,7 @@ /* For all the swcodec targets */ #if CONFIG_CODEC == SWCODEC mpegplayer +espeak #endif #endif /* IRIVER_IFP7XX_SERIES */ Index: apps/plugins/rockpaint.c =================================================================== --- apps/plugins/rockpaint.c (revision 14627) +++ apps/plugins/rockpaint.c (working copy) @@ -2915,7 +2915,9 @@ bm.data = (char*)save_buffer; ret = rb->read_bmp_file( file, &bm, ROWS*COLS*sizeof( fb_data ), - FORMAT_NATIVE ); + FORMAT_NATIVE, + COLS, ROWS, + BMP_RESIZE_INCREASE | BMP_RESIZE_DECREASE); if((bm.width > COLS ) || ( bm.height > ROWS )) return -1; Index: apps/plugins/properties.c =================================================================== --- apps/plugins/properties.c (revision 14627) +++ apps/plugins/properties.c (working copy) @@ -100,7 +100,7 @@ #if (CONFIG_CODEC == SWCODEC) int fd = rb->open(selected_file, O_RDONLY); if (fd >= 0 && - rb->get_metadata(&id3, fd, selected_file, false)) + rb->get_metadata(&id3, fd, selected_file, false, false)) #else if (!rb->mp3info(&id3, selected_file, false)) #endif Index: apps/onplay.c =================================================================== --- apps/onplay.c (revision 14627) +++ apps/onplay.c (working copy) @@ -586,6 +586,28 @@ } #endif +#ifdef HAVE_LCD_BITMAP +static bool set_splash(void) +{ + static struct tree_context *tree; + tree = tree_get_context(); + //tree->currdir + //int len = rb->strlen(tree->currdir); + char buff[MAX_PATH+1]; + //strncpy(buff, "%s/%s", tree->currdir,(char *)global_settings.splash_file, MAX_PATH ); + sprintf(buff, "%s/%s", tree->currdir, selected_file); + + + //set_file(selected_file, buff, MAX_PATH); + //set_file(selected_file, (char *)global_settings.splash_file, MAX_FILENAME); + set_file(buff, (char *)global_settings.splash_file, MAX_FILENAME); + + /* will display the file that will show when set */ + show_splash(); + return true; +} +#endif + static bool rename_file(void) { char newname[MAX_PATH]; @@ -898,7 +920,6 @@ } if (clipboard_is_copy) { - gui_syncsplash(0, ID2P(LANG_COPYING)); } else { @@ -1070,6 +1091,10 @@ MENUITEM_FUNCTION(set_backdrop_item, 0, ID2P(LANG_SET_AS_BACKDROP), set_backdrop, NULL, clipboard_callback, Icon_NOICON); #endif +#ifdef HAVE_LCD_BITMAP +MENUITEM_FUNCTION(set_splash_item, 0, ID2P(LANG_SET_AS_SPLASH), + set_splash, NULL, clipboard_callback, Icon_NOICON); +#endif #ifdef HAVE_RECORDING static bool set_recdir(void) { @@ -1131,6 +1156,23 @@ return ACTION_EXIT_MENUITEM; } #endif +#ifdef HAVE_LCD_BITMAP + else if ((this_item == &set_splash_item) && (global_settings.splash_timeout > 0)) + { + if (selected_file) + { + char *suffix = strrchr(selected_file, '.'); + if (suffix) + { + if (strcasecmp(suffix, ".bmp") == 0) + { + return action; + } + } + } + return ACTION_EXIT_MENUITEM; + } +#endif else if ((selected_file_attr & ATTR_DIRECTORY)) { if ((this_item == &delete_dir_item) @@ -1164,7 +1206,9 @@ MAKE_ONPLAYMENU( wps_onplay_menu, ID2P(LANG_ONPLAY_MENU_TITLE), onplaymenu_callback, Icon_Audio, - &sound_settings, &wps_playlist_menu, &cat_playlist_menu, + &wps_playlist_menu, &sound_settings, &cat_playlist_menu, + //&sound_settings, &wps_playlist_menu, &cat_playlist_menu, + #ifdef HAVE_TAGCACHE &rating_item, #endif @@ -1185,6 +1229,9 @@ #if LCD_DEPTH > 1 &set_backdrop_item, #endif +#ifdef HAVE_LCD_BITMAP + &set_splash_item, +#endif &list_viewers_item, &create_dir_item, &properties_item, #ifdef HAVE_RECORDING &set_recdir_item, Index: apps/gui/gwps-common.c =================================================================== --- apps/gui/gwps-common.c (revision 14627) +++ apps/gui/gwps-common.c (working copy) @@ -73,6 +73,8 @@ if (draw) gui_statusbar_draw(wps->statusbar, force); } + +static bool wps_data_albumart_load(struct gui_wps *gwps); #else #define gui_wps_statusbar_draw(wps, force) \ gui_statusbar_draw((wps)->statusbar, (force)) @@ -364,6 +366,9 @@ gwps->display->stop_scroll(); gwps->state->id3 = audio_current_track(); + /* track has changed : update the album art information */ + wps_data_albumart_load(gwps); + if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type && strcmp(gwps->state->id3->path, curr_cue->audio_filename)) { @@ -555,6 +560,90 @@ display->set_drawmode(DRMODE_SOLID); } +static bool wps_data_albumart_load(struct gui_wps *gwps) +{ + if(!gwps || !gwps->data) + return false; + + if(gwps->data->wps_uses_albumart == WPS_ALBUMART_NONE) + return false; + + if(!gwps->state->id3->albumart_found) + return false; + + struct wps_data * data = gwps->data; + + if(0 == strcasecmp(gwps->state->id3->albumart_path, data->cur_displayed_albumart_path)) + { + /* new bitmap is same as old bitmap - trivially succeed */ + gwps->state->id3->albumart_data = (fb_data *)data->img_buf_ptr; + gwps->state->id3->albumart_width = data->cur_displayed_albumart_width; + gwps->state->id3->albumart_height = data->cur_displayed_albumart_height; + + return true; + } + + int rc; + struct bitmap temp_bitmap; + temp_bitmap.data = data->img_buf_ptr; + rc = read_bmp_file(gwps->state->id3->albumart_path, + &temp_bitmap, data->img_buf_free, + FORMAT_ANY|FORMAT_TRANSPARENT, + gui_wps->data->albumart_max_width, gui_wps->data->albumart_max_height, + BMP_RESIZE_INCREASE | BMP_RESIZE_DECREASE); + + if(rc <= 0) + { + /* load failed */ + gwps->state->id3->albumart_data = NULL; + data->cur_displayed_albumart_path[0] = '\0'; + return false; + } + + data->cur_displayed_albumart_width = gwps->state->id3->albumart_width = temp_bitmap.width; + data->cur_displayed_albumart_height = gwps->state->id3->albumart_height = temp_bitmap.height; + gwps->state->id3->albumart_data = (fb_data *)temp_bitmap.data; + strcpy(data->cur_displayed_albumart_path, gwps->state->id3->albumart_path); + return true; +} + +static void draw_album_art(struct gui_wps *gwps) +{ + if(!gwps || !gwps->data || !gwps->display) + return; + + struct wps_data * data = gwps->data; + if(data->wps_uses_albumart == WPS_ALBUMART_NONE) + return; + + if(!gwps->state->id3->albumart_found || gwps->state->id3->albumart_data == NULL) + return; + + short x = data->albumart_x; + short y = data->albumart_y; + if(data->albumart_max_width>0) + { + if (data->albumart_xalign & WPS_ALBUMART_ALIGN_RIGHT) + x += data->albumart_max_width - gwps->state->id3->albumart_width; + else if (data->albumart_xalign & WPS_ALBUMART_ALIGN_CENTER) + x += (data->albumart_max_width - gwps->state->id3->albumart_width)/2; + } + if(data->albumart_max_height>0) + { + if (data->albumart_yalign & WPS_ALBUMART_ALIGN_BOTTOM) + y += data->albumart_max_height - gwps->state->id3->albumart_height; + else if (data->albumart_yalign & WPS_ALBUMART_ALIGN_CENTER) + y += (data->albumart_max_height - gwps->state->id3->albumart_height)/2; + } + + gwps->display->set_drawmode(DRMODE_FG); + gwps->display->bitmap((fb_data*)gwps->state->id3->albumart_data, + x, y, + gwps->state->id3->albumart_width, + gwps->state->id3->albumart_height ); + gwps->display->set_drawmode(DRMODE_SOLID); +} + #else /* HAVE_LCD_CHARCELL */ static bool draw_player_progress(struct gui_wps *gwps) @@ -928,7 +1017,18 @@ case WPS_TOKEN_METADATA_COMMENT: return id3->comment; - + + case WPS_TOKEN_ALBUMART_DISPLAY: + draw_album_art(gwps); + return NULL; + + case WPS_TOKEN_ALBUMART_FOUND: + if(id3->albumart_found) { + snprintf(buf, buf_size, "C"); + return buf; + } + return NULL; + case WPS_TOKEN_FILE_BITRATE: if(id3->bitrate) snprintf(buf, buf_size, "%d", id3->bitrate); Index: apps/gui/gwps.c =================================================================== --- apps/gui/gwps.c (revision 14627) +++ apps/gui/gwps.c (working copy) @@ -794,3 +794,20 @@ 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; +} + +/* vi: set ts=4 sts=4 sw=4 et ai: */ Index: apps/gui/gwps.h =================================================================== --- apps/gui/gwps.h (revision 14627) +++ apps/gui/gwps.h (working copy) @@ -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,19 @@ short progress_start; short progress_end; bool peak_meter_enabled; + + /* Album art additions */ + unsigned char wps_uses_albumart; /* WPS_ALBUMART_NONE, WPS_ALBUMART_CHECK, WPS_ALBUMART_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; + char cur_displayed_albumart_path[MAX_PATH]; /* Information about the */ + short cur_displayed_albumart_width; /* currently displayed */ + short cur_displayed_albumart_height; /* album art bitmap */ + #else /*HAVE_LCD_CHARCELLS */ unsigned short wps_progress_pat[8]; bool full_line_progressbar; @@ -417,4 +447,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/backdrop.c =================================================================== --- apps/gui/backdrop.c (revision 14627) +++ apps/gui/backdrop.c (working copy) @@ -50,7 +50,9 @@ /* load the image */ bm.data=(char*)backdrop_buffer; ret = read_bmp_file(filename, &bm, sizeof(main_backdrop), - FORMAT_NATIVE | FORMAT_DITHER); + FORMAT_NATIVE | FORMAT_DITHER, + LCD_WIDTH, LCD_HEIGHT, + BMP_RESIZE_INCREASE | BMP_RESIZE_DECREASE); if ((ret > 0) && (bm.width == LCD_WIDTH) && (bm.height == LCD_HEIGHT)) { @@ -112,7 +114,9 @@ /* load the image */ bm.data=(char*)backdrop_buffer; ret = read_bmp_file(filename, &bm, sizeof(main_backdrop), - FORMAT_NATIVE | FORMAT_DITHER | FORMAT_REMOTE); + FORMAT_NATIVE | FORMAT_DITHER | FORMAT_REMOTE, + LCD_REMOTE_WIDTH, LCD_REMOTE_HEIGHT, + BMP_RESIZE_INCREASE | BMP_RESIZE_DECREASE); if ((ret > 0) && (bm.width == LCD_REMOTE_WIDTH) && (bm.height == LCD_REMOTE_HEIGHT)) { Index: apps/gui/wps_parser.c =================================================================== --- apps/gui/wps_parser.c (revision 14627) +++ apps/gui/wps_parser.c (working copy) @@ -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 @@ -347,7 +353,9 @@ int ret = read_bmp_file(filename, bm, wps_data->img_buf_free, - format); + format, + 0,0, + BMP_RESIZE_NONE); if (ret > 0) { @@ -606,6 +614,191 @@ #endif } +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 = 0; + wps_data->albumart_yalign = 0; + + /* 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: e.g. %Cl|7|59\n (no | after y coordinate) */ + + /* 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: e.g. %Cl|7|59|200\n (no | after width field) */ + } + + /* 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 e.g. %Cl|7|59|200|200\n (no closing |) */ + } + + /* 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; + + return( _pos - wps_bufptr + 1 ); +} + +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; +}; + + /* 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) { @@ -898,6 +1091,9 @@ wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */ wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */ wps_data->peak_meter_enabled = false; + wps_data->cur_displayed_albumart_path[0] = '\0'; + wps_data->cur_displayed_albumart_width = -1; + wps_data->cur_displayed_albumart_height = -1; #else /* HAVE_LCD_CHARCELLS */ int i; for (i = 0; i < 8; i++) @@ -915,6 +1111,7 @@ bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */ #endif memset(data, 0, sizeof(*data)); + data->wps_uses_albumart = WPS_ALBUMART_NONE; wps_data_init(data); #ifdef HAVE_REMOTE_LCD data->remote_wps = rwps; Index: apps/gui/icon.c =================================================================== --- apps/gui/icon.c (revision 14627) +++ apps/gui/icon.c (working copy) @@ -220,7 +220,9 @@ char path[MAX_PATH]; snprintf(path, sizeof(path), "%s/%s.bmp", ICON_DIR, filename); - size_read = read_bmp_file(path, bmp, IMG_BUFSIZE, bmpformat); + size_read = read_bmp_file(path, bmp, IMG_BUFSIZE, bmpformat, + 0, 0, + BMP_RESIZE_NONE); if (size_read > 0) { *loaded_ok = true; Index: apps/settings.h =================================================================== --- apps/settings.h (revision 14627) +++ apps/settings.h (working copy) @@ -498,6 +498,11 @@ #ifdef HAVE_LCD_BITMAP bool offset_out_of_view; int screen_scroll_step; + /* custom user splash screen */ + int splash_timeout; /* splash screen off timeout: 0-9 0=never, + then according to timeout_values[] */ + unsigned char splash_file[MAX_PATH+1]; /* splash screen bitmap file */ +/* custom user splash screen */ #endif /* auto bookmark settings */ Index: apps/menus/display_menu.c =================================================================== --- apps/menus/display_menu.c (revision 14627) +++ apps/menus/display_menu.c (working copy) @@ -577,13 +577,19 @@ #endif MENUITEM_SETTING(show_icons, &global_settings.show_icons, NULL); +#ifdef HAVE_LCD_BITMAP +/* custom user splash screen */ +/* MENUITEM_SETTING(show_splash, &global_settings.show_splash, NULL); */ +MENUITEM_SETTING(splash_timeout, &global_settings.splash_timeout, NULL); +/* custom user splash screen */ +#endif MENUITEM_SETTING(codepage_setting, &global_settings.default_codepage, NULL); MAKE_MENU(display_menu, ID2P(LANG_DISPLAY), NULL, Icon_Display_menu, #ifdef HAVE_LCD_BITMAP - &browse_fonts, + &splash_timeout, &browse_fonts, #endif &browse_wps, #ifdef HAVE_REMOTE_LCD Index: apps/metadata.c =================================================================== --- apps/metadata.c (revision 14627) +++ apps/metadata.c (working copy) @@ -28,6 +28,10 @@ #include "logf.h" #include "cuesheet.h" +#ifdef HAVE_LCD_BITMAP +#include "gwps.h" +#endif + #if CONFIG_CODEC == SWCODEC #include "metadata/metadata_common.h" @@ -90,11 +94,164 @@ return AFMT_UNKNOWN; } +#ifdef HAVE_LCD_BITMAP +/* 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); +} + +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 albumart bitmap in the same dir as the track and in its parent dir; + * stores the found filename in the track->albumart_path. + * Returns true if a bitmap was found, false otherwise */ +static bool find_albumart(struct mp3entry* id3, const char* trackname) +{ + char path[MAX_PATH + 1]; + char dir[MAX_PATH + 1]; + bool found = false; + + if (!id3 || !trackname) + return false; + + strcpy(id3->albumart_path, ""); + + strip_filename(dir, sizeof(dir), trackname); + + DEBUGF("Looking for album art for %s\n", trackname); + + /* the first file we look for is one specific to the track playing */ + strip_extension(path, sizeof(path) - 4, trackname); + 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.bmp", + (strlen(dir) >= 1) ? dir : "", + id3->album); + 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.bmp", + (strlen(dir) >= 1) ? dir : ""); + 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.bmp", + (strlen(dir) >= 1) ? dir : "", + id3->album); + 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.bmp", + (strlen(dir) >= 1) ? dir : ""); + path[sizeof(path)-1] = 0; + found = file_exists(path); + } + + if (!found) + return false; + + strcpy(id3->albumart_path, path); + DEBUGF("Album art found for %s : %s\n", trackname, path); + return true; +} +#endif + /* Get metadata for track - return false if parsing showed problems with the * file that would prevent playback. */ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname, - bool v1first) + bool v1first, bool search_album_art) { #if CONFIG_CODEC == SWCODEC unsigned char* buf; @@ -313,6 +470,11 @@ id3->cuesheet_type = 1; } #endif + +#ifdef HAVE_LCD_BITMAP + if (search_album_art && gui_sync_wps_uses_albumart()) + id3->albumart_found = find_albumart(id3, trackname); +#endif lseek(fd, 0, SEEK_SET); strncpy(id3->path, trackname, sizeof(id3->path)); Index: apps/metadata.h =================================================================== --- apps/metadata.h (revision 14627) +++ apps/metadata.h (working copy) @@ -24,7 +24,7 @@ unsigned int probe_file_format(const char *filename); bool get_metadata(struct mp3entry* id3, int fd, const char* trackname, - bool v1first); + bool v1first, bool search_album_art); #endif Index: apps/settings_list.c =================================================================== --- apps/settings_list.c (revision 14627) +++ apps/settings_list.c (working copy) @@ -207,6 +207,34 @@ #endif /* HAVE_RECORDING */ +#ifdef HAVE_LCD_BITMAP +/* for custom user splash screen */ +static const char splash_times_conf [] = + "off,1,2,3,4,5,6,7,8,9"; +static const int splash_times[] = + {-1, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static void splash_formatter(char *buffer, size_t buffer_size, + int val, const char *unit) +{ + (void)unit; + if (val == 0) + strcpy(buffer, str(LANG_OFF)); + else if (val == 1) + strcpy(buffer, str(LANG_ON)); + else + snprintf(buffer, buffer_size, "%d s", splash_times[val]); +} +static long splash_getlang(int value) +{ + if (value == 0) + return LANG_OFF; + else if (value == 1) + return LANG_ON; + return TALK_ID(splash_times[value], UNIT_SEC); +} +/* for custom user splash screen */ +#endif + #ifdef HAVE_BACKLIGHT static const char backlight_times_conf [] = "off,on,1,2,3,4,5,6,7,8,9,10,15,20,25,30,45,60,90"; @@ -476,6 +504,14 @@ "time format", "24hour,12hour", NULL, 2, ID2P(LANG_24_HOUR_CLOCK), ID2P(LANG_12_HOUR_CLOCK)), #endif + +/* custom user splash screen */ + INT_SETTING_W_CFGVALS(F_FLIPLIST, splash_timeout, LANG_SHOW_SPLASH, 0, + "splash timeout", splash_times_conf, UNIT_SEC, + 0, 9, 1, splash_formatter, splash_getlang, + NULL), +/* custom user splash screen */ + #endif /* HAVE_LCD_BITMAP */ OFFON_SETTING(0,show_icons, LANG_SHOW_ICONS ,true,"show icons", NULL), /* system */ @@ -1183,6 +1219,9 @@ "", BACKDROP_DIR "/", ".bmp", MAX_FILENAME+1), #endif #ifdef HAVE_LCD_BITMAP + FILENAME_SETTING(F_THEMESETTING,splash_file,"splash screen", + "", "", ".bmp", MAX_PATH+1), + //"", BACKDROP_DIR "/", ".bmp", MAX_FILENAME+1), FILENAME_SETTING(0,kbd_file,"kbd","",ROCKBOX_DIR "/",".kbd",MAX_FILENAME+1), #endif #ifdef HAVE_USB_POWER Index: apps/tagcache.c =================================================================== --- apps/tagcache.c (revision 14627) +++ apps/tagcache.c (working copy) @@ -1660,7 +1660,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.id3), fd, path, false); + ret = get_metadata(&(track.id3), fd, path, false, false); close(fd); if (!ret) Index: apps/tagcache.h =================================================================== --- apps/tagcache.h (revision 14627) +++ apps/tagcache.h (working copy) @@ -72,11 +72,11 @@ #define TAGCACHE_MAX_CLAUSES 32 /* Tag database files. */ -#define TAGCACHE_FILE_TEMP ROCKBOX_DIR "/database_tmp.tcd" -#define TAGCACHE_FILE_MASTER ROCKBOX_DIR "/database_idx.tcd" -#define TAGCACHE_FILE_INDEX ROCKBOX_DIR "/database_%d.tcd" -#define TAGCACHE_FILE_CHANGELOG ROCKBOX_DIR "/database_changelog.txt" -#define TAGCACHE_STATEFILE ROCKBOX_DIR "/database_state.tcd" +#define TAGCACHE_FILE_TEMP ROCKBOX_DIR "/database/database_tmp.tcd" +#define TAGCACHE_FILE_MASTER ROCKBOX_DIR "/database/database_idx.tcd" +#define TAGCACHE_FILE_INDEX ROCKBOX_DIR "/database/database_%d.tcd" +#define TAGCACHE_FILE_CHANGELOG ROCKBOX_DIR "/database/database_changelog.txt" +#define TAGCACHE_STATEFILE ROCKBOX_DIR "/database/database_state.tcd" /* Flags */ #define FLAG_DELETED 0x0001 /* Entry has been removed from db */ Index: apps/playback.c =================================================================== --- apps/playback.c (revision 14627) +++ apps/playback.c (working copy) @@ -2744,7 +2744,7 @@ /* Get track metadata if we don't already have it. */ if (!tracks[track_widx].taginfo_ready) { - if (get_metadata(&(tracks[track_widx].id3),current_fd,trackname,v1first)) + if (get_metadata(&(tracks[track_widx].id3),current_fd,trackname,v1first,true)) { tracks[track_widx].taginfo_ready = true; if (start_play) @@ -2886,7 +2886,7 @@ if (fd < 0) return false; - status = get_metadata(&(tracks[next_idx].id3),fd,trackname,v1first); + status = get_metadata(&(tracks[next_idx].id3),fd,trackname,v1first,true); /* Preload the glyphs in the tags */ if (status) { Index: apps/plugin.c =================================================================== --- apps/plugin.c (revision 14627) +++ apps/plugin.c (working copy) @@ -198,6 +198,7 @@ gui_synclist_set_title, /* button */ + button_available, button_get, button_get_w_tmo, button_status, @@ -235,6 +236,18 @@ mkdir, rmdir, + /* talking */ + is_voice_queued, + talk_id, + talk_file, + talk_number, + talk_value, + talk_spell, + talk_menus_enabled, + talk_disable_menus, + talk_enable_menus, + do_shutup, + /* kernel/ system */ PREFIX(sleep), yield, Index: apps/plugin.h =================================================================== --- apps/plugin.h (revision 14627) +++ apps/plugin.h (working copy) @@ -285,6 +285,7 @@ void (*gui_synclist_set_title)(struct gui_synclist *lists, char* title, int icon); /* button */ + int (*button_available)(void); long (*button_get)(bool block); long (*button_get_w_tmo)(int ticks); int (*button_status)(void); @@ -324,6 +325,18 @@ int (*mkdir)(const char *name); int (*rmdir)(const char *name); + /* talking */ + bool (*is_voice_queued)(void); /* Are there more voice clips to be spoken? */ + int (*talk_id)(long id, bool enqueue); /* play a voice ID from voicefont */ + int (*talk_file)(const char* filename, bool enqueue); /* play a thumbnail from file */ + int (*talk_number)(long n, bool enqueue); /* say a number */ + int (*talk_value)(long n, int unit, bool enqueue); /* say a numeric value */ + int (*talk_spell)(const char* spell, bool enqueue); /* spell a string */ + bool (*talk_menus_enabled)(void); /* returns true if menus should be voiced */ + void (*talk_disable_menus)(void); /* disable voice menus (temporarily, not persisted) */ + void (*talk_enable_menus)(void); /* re-enable voice menus */ + int (*do_shutup)(void); /* kill voice unconditionally */ + /* kernel/ system */ void (*PREFIX(sleep))(int ticks); void (*yield)(void); @@ -591,7 +604,10 @@ #endif #ifdef HAVE_LCD_BITMAP int (*read_bmp_file)(char* filename, struct bitmap *bm, int maxsize, - int format); + int format, + int dst_maxwidth, /*=0*/ + int dst_maxheight,/*=0*/ + int flags); /*=BMP_RESIZE_NONE*/ void (*screen_dump_set_hook)(void (*hook)(int fh)); #endif int (*show_logo)(void); @@ -621,7 +637,7 @@ int (*codec_load_file)(const char* codec, struct codec_api *api); const char *(*get_codec_filename)(int cod_spec); bool (*get_metadata)(struct mp3entry* id3, int fd, const char* trackname, - bool v1first); + bool v1first, bool search_album_art); #endif void (*led)(bool on); }; Index: apps/main.c =================================================================== --- apps/main.c (revision 14627) +++ apps/main.c (working copy) @@ -272,7 +272,10 @@ lcd_remote_init(); #endif font_init(); +#ifndef HAVE_LCD_BITMAP +/* like this for the custom splash screen */ show_logo(); +#endif button_init(); backlight_init(); lang_init(); @@ -284,10 +287,20 @@ gui_syncstatusbar_init(&statusbars); settings_reset(); settings_load(SETTINGS_ALL); +#ifdef HAVE_LCD_BITMAP +/* just for the sim. This mimics the behaviour on target */ + if(global_settings.splash_timeout < 1){ + show_logo(); + }else{ + show_splash(); + } +/* custom user splash screen */ +#endif gui_sync_wps_init(); settings_apply(); init_dircache(true); init_dircache(false); + #ifdef HAVE_TAGCACHE init_tagcache(); #endif @@ -360,7 +373,11 @@ #endif font_init(); +#ifndef HAVE_LCD_BITMAP +/* like this for the custom splash screen */ show_logo(); +#endif + lang_init(); #ifdef DEBUG @@ -510,6 +527,15 @@ #endif } +#ifdef HAVE_LCD_BITMAP +/* custom user splash screen */ + if(global_settings.splash_timeout < 1){ + show_logo(); + }else{ + show_splash(); + } +#endif + gui_sync_wps_init(); settings_apply(); init_dircache(false); Index: apps/misc.c =================================================================== --- apps/misc.c (revision 14627) +++ apps/misc.c (working copy) @@ -910,7 +910,46 @@ { return default_event_handler_ex(event, NULL, NULL); } +#ifdef HAVE_LCD_BITMAP +/* custom user splash */ +int show_splash( void ) +{ +#if LCD_DEPTH >= 8 + static fb_data img_buffer[LCD_HEIGHT*LCD_WIDTH] + __attribute__ ((aligned (16))); +#elif LCD_DEPTH == 2 + static fb_data img_buffer[LCD_FBHEIGHT*LCD_FBWIDTH]; +#else + static fb_data img_buffer[LCD_WIDTH*LCD_HEIGHT]; +#endif + struct bitmap bm; + bm.data = (char*)img_buffer; + char filename[MAX_PATH]; + snprintf(filename, MAX_PATH, "%s/%s.bmp", BACKDROP_DIR, global_settings.splash_file); + int ret = read_bmp_file(filename, &bm, sizeof(img_buffer), + FORMAT_NATIVE | FORMAT_DITHER, LCD_WIDTH, LCD_HEIGHT, + BMP_RESIZE_DECREASE); + //FORMAT_NATIVE | FORMAT_DITHER); + if(ret < 0){ + ret = read_bmp_file( "/splash.bmp", &bm, sizeof(img_buffer), + FORMAT_NATIVE | FORMAT_DITHER, LCD_WIDTH, LCD_HEIGHT, + BMP_RESIZE_DECREASE); + //FORMAT_NATIVE | FORMAT_DITHER); + } + if(ret <= 0){ + show_logo(); + }else{ + lcd_bitmap(img_buffer, (LCD_WIDTH-bm.width)/2, + (LCD_HEIGHT-bm.height)/2, bm.width, bm.height); + lcd_update(); + sleep(HZ*global_settings.splash_timeout); + } + return 0; +} +/* custom user splash */ +#endif + int show_logo( void ) { #ifdef HAVE_LCD_BITMAP Index: bootloader/main-pp.c =================================================================== --- bootloader/main-pp.c (revision 14627) +++ bootloader/main-pp.c (working copy) @@ -528,7 +528,7 @@ i, pinfo->type, pinfo->size / 2048); } - if(btn & BOOTLOADER_BOOT_OF) + if(!(btn & BOOTLOADER_BOOT_OF)) { /* Load original mi4 firmware in to a memory buffer called loadbuffer. The rest of the loading is done in crt0.S. Index: firmware/export/button.h =================================================================== --- firmware/export/button.h (revision 14627) +++ firmware/export/button.h (working copy) @@ -27,6 +27,7 @@ extern struct event_queue button_queue; void button_init (void); +int button_available(void); long button_get (bool block); long button_get_w_tmo(int ticks); intptr_t button_get_data(void); Index: firmware/export/id3.h =================================================================== --- firmware/export/id3.h (revision 14627) +++ firmware/export/id3.h (working copy) @@ -22,6 +22,8 @@ #include #include "config.h" #include "file.h" +#include "lcd.h" +#include "system.h" #define ID3V2_BUF_SIZE 300 @@ -225,6 +227,15 @@ /* Cuesheet support */ int cuesheet_type; /* 0: none, 1: external, 2: embedded */ + +#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 { Index: firmware/drivers/button.c =================================================================== --- firmware/drivers/button.c (revision 14627) +++ firmware/drivers/button.c (working copy) @@ -288,6 +288,11 @@ } #endif /* HAVE_ADJUSTABLE_CPU_FREQ */ +int button_available( void ) +{ + return queue_count(&button_queue); +} + long button_get(bool block) { struct event ev; Index: uisimulator/sdl/button.c =================================================================== --- uisimulator/sdl/button.c (revision 14627) +++ uisimulator/sdl/button.c (working copy) @@ -696,6 +696,11 @@ /* Again copied from real button.c... */ +int button_available( void ) +{ + return queue_count(&button_queue); +} + long button_get(bool block) { struct event ev;