Added ID3 searching
[kugel-rb.git] / apps / wps-display.c
bloba261f21a5dacc74e930517ce15b3f8ebce5ab37b
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Björn Stenberg
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 /* ID3 formatting based on code from the MAD Winamp plugin (in_mad.dll),
21 * Copyright (C) 2000-2001 Robert Leslie.
22 * See http://www.mars.org/home/rob/proj/mpeg/ for more information.
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
29 #include "lcd.h"
30 #include "hwcompat.h"
31 #include "font.h"
32 #include "mpeg.h"
33 #include "id3.h"
34 #include "settings.h"
35 #include "playlist.h"
36 #include "kernel.h"
37 #include "system.h"
38 #include "status.h"
39 #include "wps-display.h"
40 #include "debug.h"
41 #include "mas.h"
42 #include "lang.h"
43 #include "powermgmt.h"
44 #include "sprintf.h"
45 #include "backlight.h"
47 #ifdef HAVE_LCD_BITMAP
48 #include "icons.h"
49 #include "widgets.h"
50 #include "peakmeter.h"
51 #endif
53 #define WPS_CONFIG ROCKBOX_DIR "/default.wps"
55 #ifdef HAVE_LCD_BITMAP
56 #define MAX_LINES 13
57 #define FORMAT_BUFFER_SIZE 800
58 #else
59 #define MAX_LINES 2
60 #define FORMAT_BUFFER_SIZE 400
61 #endif
62 #define MAX_SUBLINES 12
63 #define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* (10ths of sec) */
64 #define BASE_SUBLINE_TIME 10 /* base time that multiplier is applied to
65 (1/HZ sec, or 100ths of sec) */
66 #define SUBLINE_RESET -1
68 #ifdef HAVE_LCD_CHARCELLS
69 static unsigned char wps_progress_pat[8]={0,0,0,0,0,0,0,0};
70 static bool full_line_progressbar=0;
71 static bool draw_player_progress(const struct mp3entry* id3,
72 int ff_rewwind_count);
73 static void draw_player_fullbar(char* buf, int buf_size,
74 const struct mp3entry* id3,
75 int ff_rewwind_count);
76 static char map_fullbar_char(char ascii_val);
77 #endif
79 static char format_buffer[FORMAT_BUFFER_SIZE];
80 static char* format_lines[MAX_LINES][MAX_SUBLINES];
81 static unsigned char line_type[MAX_LINES][MAX_SUBLINES];
82 static unsigned short time_mult[MAX_LINES][MAX_SUBLINES];
83 static long subline_expire_time[MAX_LINES];
84 static int curr_subline[MAX_LINES];
86 static int ff_rewind_count;
87 bool wps_time_countup = true;
88 static bool wps_loaded = false;
90 /* Set format string to use for WPS, splitting it into lines */
91 static void wps_format(const char* fmt)
93 char* buf = format_buffer;
94 char* start_of_line = format_buffer;
95 int line = 0;
96 int subline;
98 strncpy(format_buffer, fmt, sizeof(format_buffer));
99 format_buffer[sizeof(format_buffer) - 1] = 0;
101 for (line=0; line<MAX_LINES; line++)
103 for (subline=0; subline<MAX_SUBLINES; subline++)
105 format_lines[line][subline] = 0;
106 time_mult[line][subline] = 0;
108 subline_expire_time[line] = 0;
109 curr_subline[line] = SUBLINE_RESET;
112 line = 0;
113 subline = 0;
114 format_lines[line][subline] = buf;
116 while ((*buf) && (line < MAX_LINES))
118 switch (*buf)
120 /* skip % sequences so "%;" doesn't start a new subline */
121 case '%':
122 buf++;
123 break;
125 case '\r': /* CR */
126 *buf = 0;
127 break;
129 case '\n': /* LF */
130 *buf = 0;
132 if (*start_of_line != '#') /* A comment? */
133 line++;
135 if (line < MAX_LINES)
137 /* the next line starts on the next byte */
138 subline = 0;
139 format_lines[line][subline] = buf+1;
140 start_of_line = format_lines[line][subline];
142 break;
144 case ';': /* start a new subline */
145 *buf = 0;
146 subline++;
147 if (subline < MAX_SUBLINES)
149 format_lines[line][subline] = buf+1;
151 else /* exceeded max sublines, skip rest of line */
153 while (*(++buf))
155 if ((*buf == '\r') || (*buf == '\n'))
157 break;
160 buf--;
161 subline = 0;
163 break;
165 buf++;
169 void wps_reset(void)
171 wps_loaded = false;
172 memset(&format_buffer, 0, sizeof format_buffer);
175 bool wps_load(const char* file, bool display)
177 int i, s;
178 char buffer[FORMAT_BUFFER_SIZE];
179 int fd;
181 fd = open(file, O_RDONLY);
183 if (fd >= 0)
185 int numread = read(fd, buffer, sizeof(buffer) - 1);
187 if (numread > 0)
189 buffer[numread] = 0;
190 wps_format(buffer);
193 close(fd);
195 if ( display ) {
196 bool any_defined_line;
197 lcd_clear_display();
198 #ifdef HAVE_LCD_BITMAP
199 lcd_setmargins(0,0);
200 #endif
201 for (s=0; s<MAX_SUBLINES; s++)
203 any_defined_line = false;
204 for (i=0; i<MAX_LINES; i++)
206 if (format_lines[i][s])
208 if (*format_lines[i][s] == 0)
209 lcd_puts(0,i," ");
210 else
211 lcd_puts(0,i,format_lines[i][s]);
212 any_defined_line = true;
214 else
216 lcd_puts(0,i," ");
219 if (any_defined_line)
221 lcd_update();
222 sleep(HZ/2);
226 wps_loaded = true;
228 return numread > 0;
231 return false;
234 /* Format time into buf.
236 * buf - buffer to format to.
237 * buf_size - size of buffer.
238 * time - time to format, in milliseconds.
240 static void format_time(char* buf, int buf_size, int time)
242 if ( time < 3600000 ) {
243 snprintf(buf, buf_size, "%d:%02d",
244 time % 3600000 / 60000, time % 60000 / 1000);
245 } else {
246 snprintf(buf, buf_size, "%d:%02d:%02d",
247 time / 3600000, time % 3600000 / 60000, time % 60000 / 1000);
251 /* Extract a part from a path.
253 * buf - buffer extract part to.
254 * buf_size - size of buffer.
255 * path - path to extract from.
256 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
257 * parent of parent, etc.
259 * Returns buf if the desired level was found, NULL otherwise.
261 static char* get_dir(char* buf, int buf_size, const char* path, int level)
263 const char* sep;
264 const char* last_sep;
265 int len;
267 sep = path + strlen(path);
268 last_sep = sep;
270 while (sep > path)
272 if ('/' == *(--sep))
274 if (!level)
276 break;
279 level--;
280 last_sep = sep - 1;
284 if (level || (last_sep <= sep))
286 return NULL;
289 len = MIN(last_sep - sep, buf_size - 1);
290 strncpy(buf, sep + 1, len);
291 buf[len] = 0;
292 return buf;
295 /* Get the tag specified by the two characters at fmt.
297 * id3 - ID3 data to get tag values from.
298 * nid3 - next-song ID3 data to get tag values from.
299 * tag - string (of two characters) specifying the tag to get.
300 * buf - buffer to certain tags, such as track number, play time or
301 * directory name.
302 * buf_size - size of buffer.
303 * flags - returns the type of the line. See constants i wps-display.h
305 * Returns the tag. NULL indicates the tag wasn't available.
307 static char* get_tag(struct mp3entry* cid3,
308 struct mp3entry* nid3,
309 const char* tag,
310 char* buf,
311 int buf_size,
312 unsigned char* tag_len,
313 unsigned short* subline_time_mult,
314 unsigned char* flags)
316 struct mp3entry *id3 = cid3; /* default to current song */
318 if ((0 == tag[0]) || (0 == tag[1]))
320 *tag_len = 0;
321 return NULL;
324 *tag_len = 2;
326 switch (tag[0])
328 case 'I': /* ID3 Information */
329 id3 = nid3; /* display next-song data */
330 *flags |= WPS_REFRESH_DYNAMIC;
331 if(!id3)
332 return NULL; /* no such info (yet) */
333 /* fall-through */
334 case 'i': /* ID3 Information */
335 *flags |= WPS_REFRESH_STATIC;
336 switch (tag[1])
338 case 't': /* ID3 Title */
339 return id3->title;
341 case 'a': /* ID3 Artist */
342 return id3->artist;
344 case 'n': /* ID3 Track Number */
345 if (id3->track_string)
346 return id3->track_string;
348 if (id3->tracknum) {
349 snprintf(buf, buf_size, "%d", id3->tracknum);
350 return buf;
352 return NULL;
354 case 'd': /* ID3 Album/Disc */
355 return id3->album;
357 case 'c': /* ID3 Composer */
358 return id3->composer;
360 case 'y': /* year */
361 if( id3->year_string )
362 return id3->year_string;
364 if (id3->year) {
365 snprintf(buf, buf_size, "%d", id3->year);
366 return buf;
368 return NULL;
370 case 'g': /* genre */
371 return id3_get_genre(id3);
373 case 'v': /* id3 version */
374 switch (id3->id3version) {
375 case ID3_VER_1_0:
376 return "1";
378 case ID3_VER_1_1:
379 return "1.1";
381 case ID3_VER_2_2:
382 return "2.2";
384 case ID3_VER_2_3:
385 return "2.3";
387 case ID3_VER_2_4:
388 return "2.4";
390 default:
391 return NULL;
394 break;
396 case 'F': /* File Information */
397 id3 = nid3;
398 *flags |= WPS_REFRESH_DYNAMIC;
399 if(!id3)
400 return NULL; /* no such info (yet) */
401 /* fall-through */
402 case 'f': /* File Information */
403 *flags |= WPS_REFRESH_STATIC;
404 switch(tag[1])
406 case 'v': /* VBR file? */
407 return id3->vbr ? "(avg)" : NULL;
409 case 'b': /* File Bitrate */
410 if(id3->bitrate)
411 snprintf(buf, buf_size, "%d", id3->bitrate);
412 else
413 snprintf(buf, buf_size, "?");
414 return buf;
416 case 'f': /* File Frequency */
417 snprintf(buf, buf_size, "%d", id3->frequency);
418 return buf;
420 case 'p': /* File Path */
421 return id3->path;
423 case 'm': /* File Name - With Extension */
424 return get_dir(buf, buf_size, id3->path, 0);
426 case 'n': /* File Name */
427 if (get_dir(buf, buf_size, id3->path, 0))
429 /* Remove extension */
430 char* sep = strrchr(buf, '.');
432 if (NULL != sep)
434 *sep = 0;
437 return buf;
439 else
441 return NULL;
444 case 's': /* File Size (in kilobytes) */
445 snprintf(buf, buf_size, "%d", id3->filesize / 1024);
446 return buf;
448 break;
450 case 'p': /* Playlist/Song Information */
451 switch(tag[1])
453 case 'b': /* progress bar */
454 *flags |= WPS_REFRESH_PLAYER_PROGRESS;
455 #ifdef HAVE_LCD_CHARCELLS
456 snprintf(buf, buf_size, "%c", wps_progress_pat[0]);
457 full_line_progressbar=0;
458 return buf;
459 #else
460 return "\x01";
461 #endif
462 case 'f': /* full-line progress bar */
463 #ifdef HAVE_LCD_CHARCELLS
464 if(is_new_player()) {
465 *flags |= WPS_REFRESH_PLAYER_PROGRESS;
466 *flags |= WPS_REFRESH_DYNAMIC;
467 full_line_progressbar=1;
468 /* we need 11 characters (full line) for
469 progress-bar */
470 snprintf(buf, buf_size, " ");
472 else
474 /* Tell the user if we have an OldPlayer */
475 snprintf(buf, buf_size, " <Old LCD> ");
477 return buf;
478 #endif
479 case 'p': /* Playlist Position */
480 *flags |= WPS_REFRESH_STATIC;
481 snprintf(buf, buf_size, "%d", playlist_get_display_index());
482 return buf;
484 case 'n': /* Playlist Name (without path) */
485 *flags |= WPS_REFRESH_STATIC;
486 return playlist_name(NULL, buf, buf_size);
488 case 'e': /* Playlist Total Entries */
489 *flags |= WPS_REFRESH_STATIC;
490 snprintf(buf, buf_size, "%d", playlist_amount());
491 return buf;
493 case 'c': /* Current Time in Song */
494 *flags |= WPS_REFRESH_DYNAMIC;
495 format_time(buf, buf_size, id3->elapsed + ff_rewind_count);
496 return buf;
498 case 'r': /* Remaining Time in Song */
499 *flags |= WPS_REFRESH_DYNAMIC;
500 format_time(buf, buf_size,
501 id3->length - id3->elapsed - ff_rewind_count);
502 return buf;
504 case 't': /* Total Time */
505 *flags |= WPS_REFRESH_STATIC;
506 format_time(buf, buf_size, id3->length);
507 return buf;
509 #ifdef HAVE_LCD_BITMAP
510 case 'm': /* Peak Meter */
511 *flags |= WPS_REFRESH_PEAK_METER;
512 return "\x01";
513 #endif
514 case 's': /* shuffle */
515 if ( global_settings.playlist_shuffle )
516 return "s";
517 else
518 return NULL;
519 break;
521 case 'v': /* volume */
522 *flags |= WPS_REFRESH_DYNAMIC;
523 snprintf(buf, buf_size, "%d%%", global_settings.volume);
524 return buf;
527 break;
529 case 'b': /* battery info */
530 *flags |= WPS_REFRESH_DYNAMIC;
531 switch (tag[1]) {
532 case 'l': /* battery level */
534 int l = battery_level();
535 if (l > -1)
536 snprintf(buf, buf_size, "%d%%", l);
537 else
538 return "?%";
539 return buf;
542 case 't': /* estimated battery time */
544 int t = battery_time();
545 if (t >= 0)
546 snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
547 else
548 strncpy(buf, "?h ?m", buf_size);
549 return buf;
552 break;
554 case 'D': /* Directory path information */
555 id3 = nid3; /* next song please! */
556 *flags |= WPS_REFRESH_DYNAMIC;
557 if(!id3)
558 return NULL; /* no such info (yet) */
559 /* fall-through */
560 case 'd': /* Directory path information */
562 int level = tag[1] - '0';
563 *flags |= WPS_REFRESH_STATIC;
564 /* d1 through d9 */
565 if ((0 < level) && (9 > level))
567 return get_dir(buf, buf_size, id3->path, level);
570 break;
572 case 't': /* set sub line time multiplier */
574 int d = 1;
575 int time_mult = 0;
576 bool have_point = false;
577 bool have_tenth = false;
579 while (((tag[d] >= '0') &&
580 (tag[d] <= '9')) ||
581 (tag[d] == '.'))
583 if (tag[d] != '.')
585 time_mult = time_mult * 10;
586 time_mult = time_mult + tag[d] - '0';
587 if (have_point)
589 have_tenth = true;
590 d++;
591 break;
594 else
596 have_point = true;
598 d++;
601 if (have_tenth == false)
602 time_mult *= 10;
604 *subline_time_mult = time_mult;
605 *tag_len = d;
607 buf[0] = 0;
608 return buf;
610 break;
613 return NULL;
616 /* Skip to the end of the current %? conditional.
618 * fmt - string to skip it. Should point to somewhere after the leading
619 * "<" char (and before or at the last ">").
620 * to_else - if true, skip to the else part (after the "|", if any), else skip
621 * to the end (the ">").
623 * Returns the new position in fmt.
625 static const char* skip_conditional(const char* fmt, bool to_else)
627 int level = 1;
629 while (*fmt)
631 switch (*fmt++)
633 case '%':
634 break;
636 case '|':
637 if (to_else && (1 == level))
638 return fmt;
640 continue;
642 case '>':
643 if (0 == --level)
645 if (to_else)
646 fmt--;
648 return fmt;
650 continue;
652 default:
653 continue;
656 switch (*fmt++)
658 case 0:
659 case '%':
660 case '|':
661 case '<':
662 case '>':
663 break;
665 case '?':
666 while (*fmt && ('<' != *fmt))
667 fmt++;
669 if ('<' == *fmt)
670 fmt++;
672 level++;
673 break;
675 default:
676 break;
680 return fmt;
683 /* Generate the display based on id3 information and format string.
685 * buf - char buffer to write the display to.
686 * buf_size - the size of buffer.
687 * id3 - the ID3 data to format with.
688 * nid3 - the ID3 data of the next song (might by NULL)
689 * fmt - format description.
690 * flags - returns the type of the line. See constants i wps-display.h
692 static void format_display(char* buf,
693 int buf_size,
694 struct mp3entry* id3,
695 struct mp3entry* nid3, /* next song's id3 */
696 const char* fmt,
697 unsigned short* subline_time_mult,
698 unsigned char* flags)
700 char temp_buf[128];
701 char* buf_start = buf;
702 char* buf_end = buf + buf_size - 1; /* Leave room for end null */
703 char* value = NULL;
704 int level = 0;
705 unsigned char tag_length;
707 *subline_time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
709 while (fmt && *fmt && buf < buf_end)
711 switch (*fmt)
713 case '%':
714 ++fmt;
715 break;
717 case '|':
718 case '>':
719 if (level > 0)
721 fmt = skip_conditional(fmt, false);
722 level--;
723 continue;
725 /* Else fall through */
727 default:
728 *buf++ = *fmt++;
729 continue;
732 switch (*fmt)
734 case 0:
735 *buf++ = '%';
736 break;
738 case 's':
739 *flags |= WPS_REFRESH_SCROLL;
740 ++fmt;
741 break;
743 case '%':
744 case '|':
745 case '<':
746 case '>':
747 case ';':
748 *buf++ = *fmt++;
749 break;
751 case '?':
752 fmt++;
753 value = get_tag(id3, nid3, fmt, temp_buf, sizeof(temp_buf),
754 &tag_length, subline_time_mult, flags);
756 while (*fmt && ('<' != *fmt))
757 fmt++;
759 if ('<' == *fmt)
760 fmt++;
762 /* No value, so skip to else part */
763 if ((!value) || (!strlen(value)))
764 fmt = skip_conditional(fmt, true);
766 level++;
767 break;
769 default:
770 value = get_tag(id3, nid3, fmt, temp_buf, sizeof(temp_buf),
771 &tag_length, subline_time_mult, flags);
772 fmt += tag_length;
774 if (value)
776 while (*value && (buf < buf_end))
777 *buf++ = *value++;
782 *buf = 0;
784 /* if resulting line is an empty line, set the subline time to 0 */
785 if (*buf_start == 0)
786 *subline_time_mult = 0;
788 /* If no flags have been set, the line didn't contain any format codes.
789 We still want to refresh it. */
790 if(*flags == 0)
791 *flags = WPS_REFRESH_STATIC;
794 bool wps_refresh(struct mp3entry* id3,
795 struct mp3entry* nid3,
796 int ffwd_offset,
797 unsigned char refresh_mode)
799 char buf[MAX_PATH];
800 unsigned char flags;
801 int i;
802 bool update_line;
803 bool only_one_subline;
804 bool new_subline_refresh;
805 int search;
806 int search_start;
807 #ifdef HAVE_LCD_BITMAP
808 int h = font_get(FONT_UI)->height;
809 int offset = global_settings.statusbar ? STATUSBAR_HEIGHT : 0;
810 /* to find out wether the peak meter is enabled we
811 assume it wasn't until we find a line that contains
812 the peak meter. We can't use peak_meter_enabled itself
813 because that would mean to turn off the meter thread
814 temporarily. (That shouldn't matter unless yield
815 or sleep is called but who knows...)
817 bool enable_pm = false;
818 #endif
820 /* reset to first subline if refresh all flag is set */
821 if (refresh_mode == WPS_REFRESH_ALL)
823 for (i=0; i<MAX_LINES; i++)
825 curr_subline[i] = SUBLINE_RESET;
829 #ifdef HAVE_LCD_CHARCELLS
830 for (i=0; i<8; i++) {
831 if (wps_progress_pat[i]==0)
832 wps_progress_pat[i]=lcd_get_locked_pattern();
834 #endif
836 if (!id3)
838 lcd_stop_scroll();
839 return false;
842 ff_rewind_count = ffwd_offset;
844 for (i = 0; i < MAX_LINES; i++)
846 new_subline_refresh = false;
847 only_one_subline = false;
849 /* if time to advance to next sub-line */
850 if (TIME_AFTER(current_tick, subline_expire_time[i] - 1) ||
851 (curr_subline[i] == SUBLINE_RESET))
853 /* search all sublines until the next subline with time > 0
854 is found or we get back to the subline we started with */
855 if (curr_subline[i] == SUBLINE_RESET)
856 search_start = 0;
857 else
858 search_start = curr_subline[i];
859 for (search=0; search<MAX_SUBLINES; search++)
861 curr_subline[i]++;
863 /* wrap around if beyond last defined subline or MAX_SUBLINES */
864 if ((!format_lines[i][curr_subline[i]]) ||
865 (curr_subline[i] == MAX_SUBLINES))
867 if (curr_subline[i] == 1)
868 only_one_subline = true;
869 curr_subline[i] = 0;
872 /* if back where we started after search or
873 only one subline is defined on the line */
874 if (((search > 0) && (curr_subline[i] == search_start)) ||
875 only_one_subline)
877 /* no other subline with a time > 0 exists */
878 subline_expire_time[i] = current_tick + 100 * HZ;
879 break;
881 else
883 /* get initial time multiplier and
884 line type flags for this subline */
885 format_display(buf, sizeof(buf), id3, nid3,
886 format_lines[i][curr_subline[i]],
887 &time_mult[i][curr_subline[i]],
888 &line_type[i][curr_subline[i]]);
890 /* only use this subline if subline time > 0 */
891 if (time_mult[i][curr_subline[i]] > 0)
893 new_subline_refresh = true;
894 subline_expire_time[i] = current_tick +
895 BASE_SUBLINE_TIME * time_mult[i][curr_subline[i]];
896 break;
903 update_line = false;
905 if ( !format_lines[i][curr_subline[i]] )
906 break;
908 if ((line_type[i][curr_subline[i]] & refresh_mode) ||
909 (refresh_mode == WPS_REFRESH_ALL) ||
910 new_subline_refresh)
912 flags = 0;
913 format_display(buf, sizeof(buf), id3, nid3,
914 format_lines[i][curr_subline[i]],
915 &time_mult[i][curr_subline[i]],
916 &flags);
917 line_type[i][curr_subline[i]] = flags;
919 #ifdef HAVE_LCD_BITMAP
920 /* progress */
921 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
922 scrollbar(0, i*h + offset + 1, LCD_WIDTH, 6,
923 id3->length?id3->length:1, 0,
924 id3->length?id3->elapsed + ff_rewind_count:0,
925 HORIZONTAL);
926 update_line = true;
928 if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) {
929 /* peak meter */
930 int peak_meter_y;
932 update_line = true;
933 peak_meter_y = i * h + offset;
935 /* The user might decide to have the peak meter in the last
936 line so that it is only displayed if no status bar is
937 visible. If so we neither want do draw nor enable the
938 peak meter. */
939 if (peak_meter_y + h <= LCD_HEIGHT) {
940 /* found a line with a peak meter -> remember that we must
941 enable it later */
942 enable_pm = true;
943 peak_meter_draw(0, peak_meter_y, LCD_WIDTH,
944 MIN(h, LCD_HEIGHT - peak_meter_y));
947 #else
948 /* progress */
949 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
950 if (full_line_progressbar)
951 draw_player_fullbar(buf, sizeof(buf),
952 id3, ff_rewind_count);
953 else
954 draw_player_progress(id3, ff_rewind_count);
956 #endif
958 if (flags & WPS_REFRESH_SCROLL) {
960 /* scroll line */
961 if ((refresh_mode & WPS_REFRESH_SCROLL) ||
962 new_subline_refresh) {
963 lcd_puts_scroll(0, i, buf);
964 update_line = true;
967 else if (flags & (WPS_REFRESH_DYNAMIC | WPS_REFRESH_STATIC))
969 /* dynamic / static line */
970 if ((refresh_mode & (WPS_REFRESH_DYNAMIC|WPS_REFRESH_STATIC)) ||
971 new_subline_refresh)
973 update_line = true;
974 lcd_puts(0, i, buf);
978 #ifdef HAVE_LCD_BITMAP
979 if (update_line) {
980 lcd_update_rect(0, i*h + offset, LCD_WIDTH, h);
982 #endif
984 #ifdef HAVE_LCD_BITMAP
985 /* Now we know wether the peak meter is used.
986 So we can enable / disable the peak meter thread */
987 peak_meter_enabled = enable_pm;
988 #endif
990 #if defined(CONFIG_BACKLIGHT) && !defined(SIMULATOR)
991 if (global_settings.caption_backlight && id3) {
992 /* turn on backlight n seconds before track ends, and turn it off n
993 seconds into the new track. n == backlight_timeout, or 5s */
994 int n =
995 backlight_timeout_value[global_settings.backlight_timeout] * 1000;
997 if ( n < 1000 )
998 n = 5000; /* use 5s if backlight is always on or off */
1000 if ((id3->elapsed < 1000) ||
1001 ((id3->length - id3->elapsed) < (unsigned)n))
1002 backlight_on();
1004 #endif
1005 return true;
1008 bool wps_display(struct mp3entry* id3,
1009 struct mp3entry* nid3)
1011 lcd_clear_display();
1013 if (!id3 && !(mpeg_status() & MPEG_STATUS_PLAY))
1015 #ifdef HAVE_LCD_CHARCELLS
1016 lcd_puts(0, 0, str(LANG_END_PLAYLIST_PLAYER));
1017 #else
1018 lcd_puts(0, 2, str(LANG_END_PLAYLIST_RECORDER));
1019 lcd_update();
1020 #endif
1021 global_settings.resume_index = -1;
1022 status_draw(true);
1023 sleep(HZ);
1024 return true;
1026 else
1028 if (!wps_loaded) {
1029 if ( !format_buffer[0] ) {
1030 #ifdef HAVE_LCD_BITMAP
1031 wps_format("%s%?it<%?in<%in. |>%it|%fn>\n"
1032 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
1033 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
1034 "\n"
1035 "%pc/%pt [%pp:%pe]\n"
1036 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
1037 "%pb\n"
1038 "%pm\n");
1039 #else
1040 wps_format("%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
1041 "%pc%?ps<*|/>%pt\n");
1042 #endif
1046 yield();
1047 wps_refresh(id3, nid3, 0, WPS_REFRESH_ALL);
1048 status_draw(true);
1049 lcd_update();
1050 return false;
1053 #ifdef HAVE_LCD_CHARCELLS
1054 static bool draw_player_progress(const struct mp3entry* id3,
1055 int ff_rewwind_count)
1057 char player_progressbar[7];
1058 char binline[36];
1059 int songpos = 0;
1060 int i,j;
1062 if (!id3)
1063 return false;
1065 memset(binline, 1, sizeof binline);
1066 memset(player_progressbar, 1, sizeof player_progressbar);
1068 if(id3->elapsed >= id3->length)
1069 songpos = 0;
1070 else
1072 if(wps_time_countup == false)
1073 songpos = ((id3->elapsed - ff_rewwind_count) * 36) / id3->length;
1074 else
1075 songpos = ((id3->elapsed + ff_rewwind_count) * 36) / id3->length;
1077 for (i=0; i < songpos; i++)
1078 binline[i] = 0;
1080 for (i=0; i<=6; i++) {
1081 for (j=0;j<5;j++) {
1082 player_progressbar[i] <<= 1;
1083 player_progressbar[i] += binline[i*5+j];
1086 lcd_define_pattern(wps_progress_pat[0], player_progressbar);
1087 return true;
1090 static void draw_player_fullbar(char* buf, int buf_size,
1091 const struct mp3entry* id3,
1092 int ff_rewwind_count)
1094 int i,j,lcd_char_pos;
1096 char player_progressbar[7];
1097 char binline[36];
1098 static const char numbers[12][4][3]={
1099 {{1,1,1},{1,0,1},{1,0,1},{1,1,1}},/*0*/
1100 {{0,1,0},{1,1,0},{0,1,0},{0,1,0}},/*1*/
1101 {{1,1,1},{0,0,1},{0,1,0},{1,1,1}},/*2*/
1102 {{1,1,1},{0,0,1},{0,1,1},{1,1,1}},/*3*/
1103 {{1,0,0},{1,1,0},{1,1,1},{0,1,0}},/*4*/
1104 {{1,1,1},{1,1,0},{0,0,1},{1,1,0}},/*5*/
1105 {{1,1,1},{1,0,0},{1,1,1},{1,1,1}},/*6*/
1106 {{1,1,1},{0,0,1},{0,1,0},{1,0,0}},/*7*/
1107 {{1,1,1},{1,1,1},{1,0,1},{1,1,1}},/*8*/
1108 {{1,1,1},{1,1,1},{0,0,1},{1,1,1}},/*9*/
1109 {{0,0,0},{0,1,0},{0,0,0},{0,1,0}},/*:*/
1110 {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} /*<blank>*/
1113 int songpos = 0;
1114 int digits[6];
1115 int time;
1116 char timestr[7];
1118 for (i=0; i < buf_size; i++)
1119 buf[i] = ' ';
1121 if(id3->elapsed >= id3->length)
1122 songpos = 55;
1123 else {
1124 if(wps_time_countup == false)
1125 songpos = ((id3->elapsed - ff_rewwind_count) * 55) / id3->length;
1126 else
1127 songpos = ((id3->elapsed + ff_rewwind_count) * 55) / id3->length;
1130 time=(id3->elapsed + ff_rewind_count);
1132 memset(timestr, 0, sizeof(timestr));
1133 format_time(timestr, sizeof(timestr), time);
1134 for(lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
1135 digits[lcd_char_pos] = map_fullbar_char(timestr[lcd_char_pos]);
1138 /* build the progressbar-icons */
1139 for (lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
1140 memset(binline, 0, sizeof binline);
1141 memset(player_progressbar, 0, sizeof player_progressbar);
1143 /* make the character (progressbar & digit)*/
1144 for (i=0; i<7; i++) {
1145 for (j=0;j<5;j++) {
1146 /* make the progressbar */
1147 if (lcd_char_pos==(songpos/5)) {
1148 /* partial */
1149 if ((j<(songpos%5))&&(i>4))
1150 binline[i*5+j] = 1;
1151 else
1152 binline[i*5+j] = 0;
1154 else {
1155 if (lcd_char_pos<(songpos/5)) {
1156 /* full character */
1157 if (i>4)
1158 binline[i*5+j] = 1;
1161 /* insert the digit */
1162 if ((j<3)&&(i<4)) {
1163 if (numbers[digits[lcd_char_pos]][i][j]==1)
1164 binline[i*5+j] = 1;
1169 for (i=0; i<=6; i++) {
1170 for (j=0;j<5;j++) {
1171 player_progressbar[i] <<= 1;
1172 player_progressbar[i] += binline[i*5+j];
1176 lcd_define_pattern(wps_progress_pat[lcd_char_pos+1],player_progressbar);
1177 buf[lcd_char_pos]=wps_progress_pat[lcd_char_pos+1];
1181 /* make rest of the progressbar if necessary */
1182 if (songpos/5>5) {
1184 /* set the characters positions that use the full 5 pixel wide bar */
1185 for (lcd_char_pos=6; lcd_char_pos < (songpos/5); lcd_char_pos++)
1186 buf[lcd_char_pos] = 0x86; /* '_' */
1188 /* build the partial bar character for the tail character position */
1189 memset(binline, 0, sizeof binline);
1190 memset(player_progressbar, 0, sizeof player_progressbar);
1192 for (i=5; i<7; i++) {
1193 for (j=0;j<5;j++) {
1194 if (j<(songpos%5)) {
1195 binline[i*5+j] = 1;
1200 for (i=0; i<7; i++) {
1201 for (j=0;j<5;j++) {
1202 player_progressbar[i] <<= 1;
1203 player_progressbar[i] += binline[i*5+j];
1207 lcd_define_pattern(wps_progress_pat[7],player_progressbar);
1209 buf[songpos/5]=wps_progress_pat[7];
1213 static char map_fullbar_char(char ascii_val)
1215 if (ascii_val >= '0' && ascii_val <= '9') {
1216 return(ascii_val - '0');
1218 else if (ascii_val == ':') {
1219 return(10);
1221 else
1222 return(11); /* anything besides a number or ':' is mapped to <blank> */
1226 #endif
1228 /* -----------------------------------------------------------------
1229 * vim: et sw=4 ts=8 sts=4 tw=78