1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
19 #include "gwps-common.h"
30 #include "powermgmt.h"
33 #ifdef HAVE_LCD_CHARCELLS
37 #include "mp3_playback.h"
38 #include "backlight.h"
42 #include "scrollbar.h"
45 #ifdef HAVE_LCD_BITMAP
46 #include "peakmeter.h"
56 #ifdef HAVE_LCD_CHARCELLS
57 static bool draw_player_progress(struct gui_wps
*gwps
);
58 static void draw_player_fullbar(struct gui_wps
*gwps
,
59 char* buf
, int buf_size
);
62 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
63 /* 3% of 30min file == 54s step size */
64 #define MIN_FF_REWIND_STEP 500
66 /* Skip leading UTF-8 BOM, if present. */
67 static char* skip_utf8_bom(char* buf
)
69 unsigned char* s
= (unsigned char*) buf
;
71 if(s
[0] == 0xef && s
[1] == 0xbb && s
[2] == 0xbf)
80 * returns the image_id between
83 #ifdef HAVE_LCD_BITMAP
84 static int get_image_id(int c
)
86 if(c
>= 'a' && c
<= 'z')
88 if(c
>= 'A' && c
<= 'Z')
95 * parse the given buffer for following static tags:
96 * %x - load image for always display
97 * %X - load backdrop image
99 * %we - enable statusbar on wps regardless of the global setting
100 * %wd - disable statusbar on wps regardless of the global setting
104 * it returns true if one of these tags is found and handled
107 bool wps_data_preload_tags(struct wps_data
*data
, char *buf
,
108 const char *bmpdir
, size_t bmpdirlen
)
110 if(!data
|| !buf
) return false;
113 #ifndef HAVE_LCD_BITMAP
114 /* no bitmap-lcd == no bitmap loading */
118 buf
= skip_utf8_bom(buf
);
129 #ifdef HAVE_LCD_BITMAP
132 * if tag found then return because these two tags must be on
133 * must be on their own line
135 if(*(buf
+1) == 'd' || *(buf
+1) == 'e')
137 data
->wps_sb_tag
= true;
138 if( *(buf
+1) == 'e' )
139 data
->show_sb_on_wps
= true;
144 #ifdef HAVE_LCD_COLOR
146 /* Backdrop image - must be the same size as the LCD */
152 char imgname
[MAX_PATH
];
154 /* format: %X|filename.bmp| */
157 pos
= strchr(ptr
, '|');
159 (int)sizeof(imgname
)-ROCKBOX_DIR_LEN
-2)
161 memcpy(imgname
, bmpdir
, bmpdirlen
);
162 imgname
[bmpdirlen
] = '/';
163 memcpy(&imgname
[bmpdirlen
+1],
165 imgname
[bmpdirlen
+1+pos
-ptr
] = 0;
168 /* filename too long */
174 bm
.data
=(char*)&wps_backdrop
[0][0];
175 ret
= read_bmp_file(imgname
, &bm
,
176 sizeof(wps_backdrop
), FORMAT_NATIVE
);
178 if ((ret
> 0) && (bm
.width
== LCD_WIDTH
)
179 && (bm
.height
== LCD_HEIGHT
)) {
180 data
->has_backdrop
=true;
192 /* progress bar image */
197 char imgname
[MAX_PATH
];
199 /* format: %P|filename.bmp| */
202 pos
= strchr(ptr
, '|');
204 (int)sizeof(imgname
)-ROCKBOX_DIR_LEN
-2)
206 memcpy(imgname
, bmpdir
, bmpdirlen
);
207 imgname
[bmpdirlen
] = '/';
208 memcpy(&imgname
[bmpdirlen
+1],
210 imgname
[bmpdirlen
+1+pos
-ptr
] = 0;
213 /* filename too long */
219 data
->progressbar
.bm
.data
=data
->img_buf_ptr
;
220 ret
= read_bmp_file(imgname
, &data
->progressbar
.bm
,
222 FORMAT_ANY
|FORMAT_TRANSPARENT
);
228 /* Always consume an even number of bytes */
231 data
->img_buf_ptr
+= ret
;
232 data
->img_buf_free
-= ret
;
234 if (data
->progressbar
.bm
.width
<= LCD_WIDTH
) {
235 data
->progressbar
.have_bitmap_pb
=true;
247 /* Preload images so the %xd# tag can display it */
253 char imgname
[MAX_PATH
];
256 if (qual
== 'l' || qual
== '|') /* format:
257 %x|n|filename.bmp|x|y|
259 %xl|n|filename.bmp|x|y|
262 ptr
= strchr(ptr
, '|') + 1;
263 pos
= strchr(ptr
, '|');
266 /* get the image ID */
267 n
= get_image_id(*ptr
);
269 if(n
< 0 || n
>= MAX_IMAGES
)
271 /* Skip the rest of the line */
278 /* check the image number and load state */
279 if (data
->img
[n
].loaded
)
281 /* Skip the rest of the line */
289 pos
= strchr(ptr
, '|');
291 (int)sizeof(imgname
)-ROCKBOX_DIR_LEN
-2)
293 memcpy(imgname
, bmpdir
, bmpdirlen
);
294 imgname
[bmpdirlen
] = '/';
295 memcpy(&imgname
[bmpdirlen
+1],
297 imgname
[bmpdirlen
+1+pos
-ptr
] = 0;
300 /* filename too long */
306 pos
= strchr(ptr
, '|');
308 data
->img
[n
].x
= atoi(ptr
);
311 /* weird syntax, bail out */
318 pos
= strchr(ptr
, '|');
320 data
->img
[n
].y
= atoi(ptr
);
323 /* weird syntax, bail out */
329 data
->img
[n
].bm
.data
= data
->img_buf_ptr
;
330 ret
= read_bmp_file(imgname
, &data
->img
[n
].bm
,
332 FORMAT_ANY
|FORMAT_TRANSPARENT
);
338 /* Always consume an even number of bytes */
341 data
->img_buf_ptr
+= ret
;
342 data
->img_buf_free
-= ret
;
343 data
->img
[n
].loaded
= true;
345 data
->img
[n
].always_display
= true;
356 /* no of these tags found */
361 /* draws the statusbar on the given wps-screen */
362 #ifdef HAVE_LCD_BITMAP
363 static void gui_wps_statusbar_draw(struct gui_wps
*wps
, bool force
)
365 bool draw
= global_settings
.statusbar
;
367 if(wps
->data
->wps_sb_tag
368 && wps
->data
->show_sb_on_wps
)
370 else if(wps
->data
->wps_sb_tag
)
373 gui_statusbar_draw(wps
->statusbar
, force
);
376 #define gui_wps_statusbar_draw(wps, force) \
377 gui_statusbar_draw((wps)->statusbar, (force))
380 /* Format time into buf.
382 * buf - buffer to format to.
383 * buf_size - size of buffer.
384 * time - time to format, in milliseconds.
386 void gui_wps_format_time(char* buf
, int buf_size
, long time
)
388 if ( time
< 3600000 ) {
389 snprintf(buf
, buf_size
, "%d:%02d",
390 (int) (time
% 3600000 / 60000), (int) (time
% 60000 / 1000));
392 snprintf(buf
, buf_size
, "%d:%02d:%02d",
393 (int) (time
/ 3600000), (int) (time
% 3600000 / 60000),
394 (int) (time
% 60000 / 1000));
398 /* Extract a part from a path.
400 * buf - buffer extract part to.
401 * buf_size - size of buffer.
402 * path - path to extract from.
403 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
404 * parent of parent, etc.
406 * Returns buf if the desired level was found, NULL otherwise.
408 static char* get_dir(char* buf
, int buf_size
, const char* path
, int level
)
411 const char* last_sep
;
414 sep
= path
+ strlen(path
);
431 if (level
|| (last_sep
<= sep
))
436 len
= MIN(last_sep
- sep
, buf_size
- 1);
437 strncpy(buf
, sep
+ 1, len
);
442 /* Get the tag specified by the two characters at fmt.
444 * cid3 - ID3 data to get tag values from.
445 * nid3 - next-song ID3 data to get tag values from.
446 * tag - string (of two characters) specifying the tag to get.
447 * buf - buffer to certain tags, such as track number, play time or
449 * buf_size - size of buffer.
450 * flags - returns the type of the line. See constants i wps-display.h
452 * Returns the tag. NULL indicates the tag wasn't available.
454 static char* get_tag(struct wps_data
* wps_data
,
455 struct mp3entry
* cid3
,
456 struct mp3entry
* nid3
,
460 unsigned char* tag_len
,
461 unsigned short* subline_time_mult
,
462 unsigned char* flags
,
465 struct mp3entry
*id3
= cid3
; /* default to current song */
466 #ifndef HAVE_LCD_CHARCELLS
469 if ((0 == tag
[0]) || (0 == tag
[1]))
481 case 'I': /* ID3 Information */
482 id3
= nid3
; /* display next-song data */
483 *flags
|= WPS_REFRESH_DYNAMIC
;
485 return NULL
; /* no such info (yet) */
487 case 'i': /* ID3 Information */
488 *flags
|= WPS_REFRESH_STATIC
;
491 case 't': /* ID3 Title */
494 case 'a': /* ID3 Artist */
497 case 'n': /* ID3 Track Number */
498 if (id3
->track_string
)
499 return id3
->track_string
;
502 snprintf(buf
, buf_size
, "%d", id3
->tracknum
);
507 case 'd': /* ID3 Album/Disc */
510 case 'c': /* ID3 Composer */
511 return id3
->composer
;
514 if( id3
->year_string
)
515 return id3
->year_string
;
518 snprintf(buf
, buf_size
, "%d", id3
->year
);
523 case 'g': /* genre */
524 return id3_get_genre(id3
);
526 case 'v': /* id3 version */
527 switch (id3
->id3version
)
550 case 'F': /* File Information */
552 *flags
|= WPS_REFRESH_DYNAMIC
;
554 return NULL
; /* no such info (yet) */
556 case 'f': /* File Information */
557 *flags
|= WPS_REFRESH_STATIC
;
560 case 'v': /* VBR file? */
561 return id3
->vbr
? "(avg)" : NULL
;
563 case 'b': /* File Bitrate */
565 snprintf(buf
, buf_size
, "%d", id3
->bitrate
);
567 snprintf(buf
, buf_size
, "?");
570 case 'f': /* File Frequency */
571 snprintf(buf
, buf_size
, "%ld", id3
->frequency
);
574 case 'p': /* File Path */
577 case 'm': /* File Name - With Extension */
578 return get_dir(buf
, buf_size
, id3
->path
, 0);
580 case 'n': /* File Name */
581 if (get_dir(buf
, buf_size
, id3
->path
, 0))
583 /* Remove extension */
584 char* sep
= strrchr(buf
, '.');
598 case 's': /* File Size (in kilobytes) */
599 snprintf(buf
, buf_size
, "%ld", id3
->filesize
/ 1024);
602 case 'c': /* File Codec */
603 if(id3
->codectype
== AFMT_UNKNOWN
)
604 *intval
= AFMT_NUM_CODECS
;
606 *intval
= id3
->codectype
;
607 return id3_get_codec(id3
);
611 case 'p': /* Playlist/Song Information */
614 case 'b': /* progress bar */
615 *flags
|= WPS_REFRESH_PLAYER_PROGRESS
;
616 #ifdef HAVE_LCD_CHARCELLS
617 snprintf(buf
, buf_size
, "%c",
618 wps_data
->wps_progress_pat
[0]);
619 wps_data
->full_line_progressbar
=0;
622 char *p
=strchr(tag
, '|');
624 wps_data
->progress_height
=atoi(++p
);
627 wps_data
->progress_start
=atoi(++p
);
630 wps_data
->progress_end
=atoi(++p
);
632 wps_data
->progress_end
=0;
634 wps_data
->progress_start
=0;
635 wps_data
->progress_end
=0;
638 if (wps_data
->progress_height
<3)
639 wps_data
->progress_height
=3;
640 if (wps_data
->progress_end
<wps_data
->progress_start
+3)
641 wps_data
->progress_end
=0;
643 wps_data
->progress_height
=6;
644 wps_data
->progress_start
=0;
645 wps_data
->progress_end
=0;
649 case 'f': /* full-line progress bar */
650 #ifdef HAVE_LCD_CHARCELLS
651 if(is_new_player()) {
652 *flags
|= WPS_REFRESH_PLAYER_PROGRESS
;
653 *flags
|= WPS_REFRESH_DYNAMIC
;
654 wps_data
->full_line_progressbar
=1;
655 /* we need 11 characters (full line) for
657 snprintf(buf
, buf_size
, " ");
661 /* Tell the user if we have an OldPlayer */
662 snprintf(buf
, buf_size
, " <Old LCD> ");
666 case 'p': /* Playlist Position */
667 *flags
|= WPS_REFRESH_STATIC
;
668 snprintf(buf
, buf_size
, "%d",
669 playlist_get_display_index());
672 case 'n': /* Playlist Name (without path) */
673 *flags
|= WPS_REFRESH_STATIC
;
674 return playlist_name(NULL
, buf
, buf_size
);
676 case 'e': /* Playlist Total Entries */
677 *flags
|= WPS_REFRESH_STATIC
;
678 snprintf(buf
, buf_size
, "%d", playlist_amount());
681 case 'c': /* Current Time in Song */
682 *flags
|= WPS_REFRESH_DYNAMIC
;
683 gui_wps_format_time(buf
, buf_size
,
684 id3
->elapsed
+ wps_state
.ff_rewind_count
);
687 case 'r': /* Remaining Time in Song */
688 *flags
|= WPS_REFRESH_DYNAMIC
;
689 gui_wps_format_time(buf
, buf_size
,
690 id3
->length
- id3
->elapsed
-
691 wps_state
.ff_rewind_count
);
694 case 't': /* Total Time */
695 *flags
|= WPS_REFRESH_STATIC
;
696 gui_wps_format_time(buf
, buf_size
, id3
->length
);
699 #ifdef HAVE_LCD_BITMAP
700 case 'm': /* Peak Meter */
701 *flags
|= WPS_REFRESH_PEAK_METER
;
704 case 's': /* shuffle */
705 *flags
|= WPS_REFRESH_DYNAMIC
;
706 if ( global_settings
.playlist_shuffle
)
712 case 'v': /* volume */
713 *flags
|= WPS_REFRESH_DYNAMIC
;
714 snprintf(buf
, buf_size
, "%d", global_settings
.volume
);
715 *intval
= 10 * (global_settings
.volume
716 - sound_min(SOUND_VOLUME
))
717 / (sound_max(SOUND_VOLUME
)
718 - sound_min(SOUND_VOLUME
)) + 1;
724 #if (CONFIG_CODEC == SWCODEC)
725 case 'S': /* DSP/Equalizer/Sound settings */
728 case 'p': /* pitch */
729 *intval
= sound_get_pitch();
730 snprintf(buf
, buf_size
, "%d.%d",
731 *intval
/ 10, *intval
% 10);
740 case 'm': /* playback repeat mode */
741 *flags
|= WPS_REFRESH_DYNAMIC
;
742 *intval
= global_settings
.repeat_mode
+ 1;
743 snprintf(buf
, buf_size
, "%d", *intval
);
746 /* playback status */
748 *flags
|= WPS_REFRESH_DYNAMIC
;
749 int status
= audio_status();
751 if (status
== AUDIO_STATUS_PLAY
&& \
752 !(status
& AUDIO_STATUS_PAUSE
))
754 if (audio_status() & AUDIO_STATUS_PAUSE
&& \
755 (! status_get_ffmode()))
757 if (status_get_ffmode() == STATUS_FASTFORWARD
)
759 if (status_get_ffmode() == STATUS_FASTBACKWARD
)
761 snprintf(buf
, buf_size
, "%d", *intval
);
764 #ifdef HAS_BUTTON_HOLD
766 *flags
|= WPS_REFRESH_DYNAMIC
;
772 #ifdef HAS_REMOTE_BUTTON_HOLD
773 case 'r': /* remote hold */
774 *flags
|= WPS_REFRESH_DYNAMIC
;
775 if (remote_button_hold())
783 case 'b': /* battery info */
784 *flags
|= WPS_REFRESH_DYNAMIC
;
787 case 'l': /* battery level */
789 int l
= battery_level();
792 snprintf(buf
, buf_size
, "%d", l
);
793 *intval
= l
/ 20 + 1;
803 case 'v': /* battery voltage */
805 unsigned int v
= battery_voltage();
806 snprintf(buf
, buf_size
, "%d.%02d", v
/100, v
%100);
810 case 't': /* estimated battery time */
812 int t
= battery_time();
814 snprintf(buf
, buf_size
, "%dh %dm", t
/ 60, t
% 60);
816 strncpy(buf
, "?h ?m", buf_size
);
820 case 's': /* sleep timer */
822 if (get_sleep_timer() == 0)
828 gui_wps_format_time(buf
, buf_size
, \
829 get_sleep_timer() * 1000);
835 case 'p': /* External power plugged in? */
837 if(charger_input_state
==CHARGER
)
843 #if defined(HAVE_CHARGE_CTRL) || \
844 defined (HAVE_CHARGE_STATE) || \
845 CONFIG_BATTERY == BATT_LIION2200
846 case 'c': /* Charging */
848 if (charge_state
== CHARGING
|| charge_state
== TOPOFF
) {
858 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
859 case 'l': /* VIRTUAL_LED */
863 case 'h': /* Only one we have so far HDD LED */
864 *flags
|= WPS_REFRESH_DYNAMIC
;
874 case 'D': /* Directory path information */
875 id3
= nid3
; /* next song please! */
876 *flags
|= WPS_REFRESH_DYNAMIC
;
878 return NULL
; /* no such info (yet) */
880 case 'd': /* Directory path information */
882 int level
= tag
[1] - '0';
883 *flags
|= WPS_REFRESH_STATIC
;
885 if ((0 < level
) && (9 > level
))
887 return get_dir(buf
, buf_size
, id3
->path
, level
);
892 case 't': /* set sub line time multiplier */
896 bool have_point
= false;
897 bool have_tenth
= false;
899 while (((tag
[d
] >= '0') &&
905 time_mult
= time_mult
* 10;
906 time_mult
= time_mult
+ tag
[d
] - '0';
921 if (have_tenth
== false)
924 *subline_time_mult
= time_mult
;
931 case 'r': /* Runtime database Information */
934 case 'p': /* Playcount */
935 *flags
|= WPS_REFRESH_STATIC
;
936 snprintf(buf
, buf_size
, "%ld", cid3
->playcount
);
938 case 'r': /* Rating */
939 *flags
|= WPS_REFRESH_STATIC
;
940 *intval
= cid3
->rating
+1;
941 snprintf(buf
, buf_size
, "%d", cid3
->rating
);
946 case 'c': /* Real Time Clock display */
947 *flags
|= WPS_REFRESH_DYNAMIC
;
952 struct tm
* tm
= get_time();
954 for (i
=1;/*break*/;i
++) {
957 case 'a': /* abbreviated weekday name (Sun..Sat) */
959 if (value
> 6 || value
< 0) continue;
961 bufptr
,buf_size
,"%s",str(dayname
[value
]));
965 case 'b': /* abbreviated month name (Jan..Dec) */
967 if (value
> 11 || value
< 0) continue;
969 bufptr
,buf_size
,"%s",str(monthname
[value
]));
973 case 'd': /* day of month (01..31) */
975 if (value
> 31 || value
< 1) continue;
978 case 'e': /* day of month, blank padded ( 1..31) */
980 if (value
> 31 || value
< 1) continue;
983 case 'H': /* hour (00..23) */
985 if (value
> 23) continue;
988 case 'k': /* hour ( 0..23) */
990 if (value
> 23) continue;
993 case 'I': /* hour (01..12) */
995 if (value
> 23) continue;
997 if (value
== 0) value
= 12;
1000 case 'l': /* hour ( 1..12) */
1001 value
= tm
->tm_hour
;
1002 if (value
> 23 || value
< 0) continue;
1004 if (value
== 0) value
= 12;
1007 case 'm': /* month (01..12) */
1009 if (value
> 11 || value
< 0) continue;
1013 case 'M': /* minute (00..59) */
1015 if (value
> 59 || value
< 0) continue;
1018 case 'S': /* second (00..59) */
1020 if (value
> 59 || value
< 0) continue;
1023 case 'y': /* last two digits of year (00..99) */
1024 value
= tm
->tm_year
;
1028 case 'Y': /* year (1970...) */
1029 value
= tm
->tm_year
;
1030 if (value
> 199 || value
< 100) continue;
1034 case 'p': /* upper case AM or PM indicator */
1035 if (tm
->tm_hour
/12 == 0) format
= "AM";
1037 snprintf(bufptr
,buf_size
,"%s",format
);
1041 case 'P': /* lower case am or pm indicator */
1042 if (tm
->tm_hour
/12 == 0) format
= "am";
1044 snprintf(bufptr
,buf_size
,"%s",format
);
1048 case 'u': /* day of week (1..7); 1 is Monday */
1049 value
= tm
->tm_wday
;
1050 if (value
< 0 || value
> 6) continue;
1054 case 'w': /* day of week (0..6); 0 is Sunday */
1055 value
= tm
->tm_wday
;
1056 if (value
< 0 || value
> 6) continue;
1060 if (tag
[i
] == 'c') {
1064 } else if (tag
[i
] == '\n') {
1068 snprintf(bufptr
,buf_size
,"%c",tag
[i
]);
1073 if (value
< 0) break;
1075 value
= snprintf(bufptr
, buf_size
, format
, value
);
1082 #endif /* CONFIG_RTC */
1087 #ifdef HAVE_LCD_BITMAP
1088 /* clears the area where the image was shown */
1089 static void clear_image_pos(struct gui_wps
*gwps
, int n
)
1093 struct wps_data
*data
= gwps
->data
;
1094 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1095 gwps
->display
->fillrect(data
->img
[n
].x
, data
->img
[n
].y
,
1096 data
->img
[n
].bm
.width
, data
->img
[n
].bm
.height
);
1097 gwps
->display
->set_drawmode(DRMODE_SOLID
);
1101 /* Skip to the end of the current %? conditional.
1103 * fmt - string to skip it. Should point to somewhere after the leading
1104 * "<" char (and before or at the last ">").
1105 * num - number of |'s to skip, or 0 to skip to the end (the ">").
1107 * Returns the new position in fmt.
1109 static const char* skip_conditional(struct gui_wps
*gwps
, const char* fmt
,
1114 const char *last_alternative
= NULL
;
1115 #ifdef HAVE_LCD_BITMAP
1116 struct wps_data
*data
= NULL
;
1117 int last_x
=-1, last_y
=-1, last_w
=-1, last_h
=-1;
1128 #ifdef HAVE_LCD_BITMAP
1129 if(data
&& *(fmt
) == 'x' && *(fmt
+1) == 'd' )
1133 if(n
>= 'a' && n
<= 'z')
1135 if(n
>= 'A' && n
<= 'Z')
1137 if(last_x
!= data
->img
[n
].x
|| last_y
!= data
->img
[n
].y
1138 || last_w
!= data
->img
[n
].bm
.width
1139 || last_h
!= data
->img
[n
].bm
.height
)
1141 last_x
= data
->img
[n
].x
;
1142 last_y
= data
->img
[n
].y
;
1143 last_w
= data
->img
[n
].bm
.width
;
1144 last_h
= data
->img
[n
].bm
.height
;
1145 clear_image_pos(gwps
,n
);
1153 last_alternative
= fmt
;
1166 /* We're just skipping to the end */
1170 /* If we are parsing an enum, we'll return the selected
1171 item. If there weren't enough items in the enum, we'll
1172 return the last one found. */
1173 if(count
&& last_alternative
)
1175 return last_alternative
;
1195 while (*fmt
&& ('<' != *fmt
))
1212 /* Generate the display based on id3 information and format string.
1214 * buf - char buffer to write the display to.
1215 * buf_size - the size of buffer.
1216 * id3 - the ID3 data to format with.
1217 * nid3 - the ID3 data of the next song (might by NULL)
1218 * fmt - format description.
1219 * flags - returns the type of the line. See constants i wps-display.h
1221 static void format_display(struct gui_wps
*gwps
, char* buf
,
1223 struct mp3entry
* id3
,
1224 struct mp3entry
* nid3
, /* next song's id3 */
1226 struct align_pos
* align
,
1227 unsigned short* subline_time_mult
,
1228 unsigned char* flags
)
1231 char* buf_start
= buf
;
1232 char* buf_end
= buf
+ buf_size
- 1; /* Leave room for end null */
1235 unsigned char tag_length
;
1238 char* cur_align_start
;
1239 #ifdef HAVE_LCD_BITMAP
1240 struct gui_img
*img
= gwps
->data
->img
;
1244 cur_align_start
= buf
;
1245 cur_align
= WPS_ALIGN_LEFT
;
1246 *subline_time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
1252 while (fmt
&& *fmt
&& buf
< buf_end
)
1264 fmt
= skip_conditional(NULL
,fmt
, 0);
1268 /* Else fall through */
1282 /* remember where the current aligned text started */
1285 case WPS_ALIGN_LEFT
:
1286 align
->left
= cur_align_start
;
1289 case WPS_ALIGN_CENTER
:
1290 align
->center
= cur_align_start
;
1293 case WPS_ALIGN_RIGHT
:
1294 align
->right
= cur_align_start
;
1297 /* start a new alignment */
1301 cur_align
= WPS_ALIGN_LEFT
;
1304 cur_align
= WPS_ALIGN_CENTER
;
1307 cur_align
= WPS_ALIGN_RIGHT
;
1311 cur_align_start
= buf
;
1315 *flags
|= WPS_REFRESH_SCROLL
;
1319 case 'x': /* image support */
1320 #ifdef HAVE_LCD_BITMAP
1321 if ('d' == *(fmt
+1) )
1325 /* get the image ID */
1327 if(n
>= 'a' && n
<= 'z')
1329 if(n
>= 'A' && n
<= 'Z')
1331 if (n
>= 0 && n
< MAX_IMAGES
&& img
[n
].loaded
) {
1332 img
[n
].display
= true;
1351 value
= get_tag(gwps
->data
, id3
, nid3
, fmt
, temp_buf
,
1352 sizeof(temp_buf
),&tag_length
,
1353 subline_time_mult
, flags
, &intval
);
1355 while (*fmt
&& ('<' != *fmt
))
1361 /* No value, so skip to else part, using a sufficiently high
1362 value to "hit" the last part of the conditional */
1363 if ((!value
) || (!strlen(value
)))
1364 fmt
= skip_conditional(gwps
, fmt
, 1000);
1366 if(intval
> 1) /* enum */
1367 fmt
= skip_conditional(gwps
, fmt
, intval
- 1);
1373 value
= get_tag(gwps
->data
, id3
, nid3
, fmt
, temp_buf
,
1374 sizeof(temp_buf
), &tag_length
,
1375 subline_time_mult
, flags
,&intval
);
1380 while (*value
&& (buf
< buf_end
))
1386 /* remember where the current aligned text started */
1389 case WPS_ALIGN_LEFT
:
1390 align
->left
= cur_align_start
;
1393 case WPS_ALIGN_CENTER
:
1394 align
->center
= cur_align_start
;
1397 case WPS_ALIGN_RIGHT
:
1398 align
->right
= cur_align_start
;
1404 /* if resulting line is an empty line, set the subline time to 0 */
1405 if (buf
- buf_start
== 0)
1406 *subline_time_mult
= 0;
1408 /* If no flags have been set, the line didn't contain any format codes.
1409 We still want to refresh it. */
1411 *flags
= WPS_REFRESH_STATIC
;
1414 /* fades the volume */
1415 void fade(bool fade_in
)
1417 int fp_global_vol
= global_settings
.volume
<< 8;
1418 int fp_min_vol
= sound_min(SOUND_VOLUME
) << 8;
1419 int fp_step
= (fp_global_vol
- fp_min_vol
) / 30;
1423 int fp_volume
= fp_min_vol
;
1425 /* zero out the sound */
1426 sound_set_volume(fp_min_vol
>> 8);
1428 sleep(HZ
/10); /* let audio thread run */
1431 while (fp_volume
< fp_global_vol
- fp_step
) {
1432 fp_volume
+= fp_step
;
1433 sound_set_volume(fp_volume
>> 8);
1436 sound_set_volume(global_settings
.volume
);
1440 int fp_volume
= fp_global_vol
;
1442 while (fp_volume
> fp_min_vol
+ fp_step
) {
1443 fp_volume
-= fp_step
;
1444 sound_set_volume(fp_volume
>> 8);
1449 /* let audio thread run and wait for the mas to run out of data */
1450 while (!mp3_pause_done())
1454 /* reset volume to what it was before the fade */
1455 sound_set_volume(global_settings
.volume
);
1459 /* Set format string to use for WPS, splitting it into lines */
1460 void gui_wps_format(struct wps_data
*data
)
1462 char* buf
= data
->format_buffer
;
1463 char* start_of_line
= data
->format_buffer
;
1470 for (line
=0; line
<WPS_MAX_LINES
; line
++)
1472 for (subline
=0; subline
<WPS_MAX_SUBLINES
; subline
++)
1474 data
->format_lines
[line
][subline
] = 0;
1475 data
->time_mult
[line
][subline
] = 0;
1477 data
->subline_expire_time
[line
] = 0;
1478 data
->curr_subline
[line
] = SUBLINE_RESET
;
1483 buf
= skip_utf8_bom(buf
);
1484 data
->format_lines
[line
][subline
] = buf
;
1486 while ((*buf
) && (line
< WPS_MAX_LINES
))
1493 * skip % sequences so "%;" doesn't start a new subline
1494 * don't skip %x lines (pre-load bitmaps)
1507 if (*start_of_line
!= '#') /* A comment? */
1510 if (line
< WPS_MAX_LINES
)
1512 /* the next line starts on the next byte */
1514 data
->format_lines
[line
][subline
] = buf
+1;
1515 start_of_line
= data
->format_lines
[line
][subline
];
1519 case ';': /* start a new subline */
1522 if (subline
< WPS_MAX_SUBLINES
)
1524 data
->format_lines
[line
][subline
] = buf
+1;
1526 else /* exceeded max sublines, skip rest of line */
1530 if ((*buf
== '\r') || (*buf
== '\n'))
1544 #ifdef HAVE_LCD_BITMAP
1545 /* Display images */
1546 static void wps_draw_image(struct gui_wps
*gwps
, int n
)
1548 struct screen
*display
= gwps
->display
;
1549 struct wps_data
*data
= gwps
->data
;
1550 if(data
->img
[n
].always_display
)
1551 display
->set_drawmode(DRMODE_FG
);
1553 display
->set_drawmode(DRMODE_SOLID
);
1556 if(data
->img
[n
].bm
.format
== FORMAT_MONO
) {
1558 display
->mono_bitmap(data
->img
[n
].bm
.data
, data
->img
[n
].x
,
1559 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
1560 data
->img
[n
].bm
.height
);
1563 display
->transparent_bitmap((fb_data
*)data
->img
[n
].bm
.data
,
1565 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
1566 data
->img
[n
].bm
.height
);
1570 static void wps_display_images(struct gui_wps
*gwps
, bool always
)
1572 if(!gwps
|| !gwps
->data
|| !gwps
->display
) return;
1574 struct wps_data
*data
= gwps
->data
;
1575 struct screen
*display
= gwps
->display
;
1576 for (n
= 0; n
< MAX_IMAGES
; n
++) {
1577 if (data
->img
[n
].loaded
) {
1578 if( (!always
&& data
->img
[n
].display
)
1579 || (always
&& data
->img
[n
].always_display
) )
1580 wps_draw_image(gwps
, n
);
1583 display
->set_drawmode(DRMODE_SOLID
);
1587 void gui_wps_reset(struct gui_wps
*gui_wps
)
1589 if(!gui_wps
|| !gui_wps
->data
)
1591 gui_wps
->data
->wps_loaded
= false;
1592 memset(&gui_wps
->data
->format_buffer
, 0,
1593 sizeof(gui_wps
->data
->format_buffer
));
1596 bool gui_wps_refresh(struct gui_wps
*gwps
, int ffwd_offset
,
1597 unsigned char refresh_mode
)
1600 unsigned char flags
;
1603 bool only_one_subline
;
1604 bool new_subline_refresh
;
1607 struct wps_data
*data
= gwps
->data
;
1608 struct wps_state
*state
= gwps
->state
;
1609 struct screen
*display
= gwps
->display
;
1610 if(!gwps
|| !data
|| !state
|| !display
)
1614 #ifdef HAVE_LCD_BITMAP
1615 int h
= font_get(FONT_UI
)->height
;
1617 gui_wps_statusbar_draw(gwps
, true);
1618 if(data
->wps_sb_tag
&& data
->show_sb_on_wps
)
1619 offset
= STATUSBAR_HEIGHT
;
1620 else if ( global_settings
.statusbar
&& !data
->wps_sb_tag
)
1621 offset
= STATUSBAR_HEIGHT
;
1623 /* to find out wether the peak meter is enabled we
1624 assume it wasn't until we find a line that contains
1625 the peak meter. We can't use peak_meter_enabled itself
1626 because that would mean to turn off the meter thread
1627 temporarily. (That shouldn't matter unless yield
1628 or sleep is called but who knows...)
1630 bool enable_pm
= false;
1632 /* Set images to not to be displayed */
1633 for (i
= 0; i
< MAX_IMAGES
; i
++) {
1634 data
->img
[i
].display
= false;
1637 /* reset to first subline if refresh all flag is set */
1638 if (refresh_mode
== WPS_REFRESH_ALL
)
1640 for (i
=0; i
<WPS_MAX_LINES
; i
++)
1642 data
->curr_subline
[i
] = SUBLINE_RESET
;
1646 #ifdef HAVE_LCD_CHARCELLS
1647 for (i
=0; i
<8; i
++) {
1648 if (data
->wps_progress_pat
[i
]==0)
1649 data
->wps_progress_pat
[i
]=display
->get_locked_pattern();
1655 display
->stop_scroll();
1659 state
->ff_rewind_count
= ffwd_offset
;
1661 for (i
= 0; i
< WPS_MAX_LINES
; i
++)
1663 new_subline_refresh
= false;
1664 only_one_subline
= false;
1666 /* if time to advance to next sub-line */
1667 if (TIME_AFTER(current_tick
, data
->subline_expire_time
[i
] - 1) ||
1668 (data
->curr_subline
[i
] == SUBLINE_RESET
))
1670 /* search all sublines until the next subline with time > 0
1671 is found or we get back to the subline we started with */
1672 if (data
->curr_subline
[i
] == SUBLINE_RESET
)
1675 search_start
= data
->curr_subline
[i
];
1676 for (search
=0; search
<WPS_MAX_SUBLINES
; search
++)
1678 data
->curr_subline
[i
]++;
1680 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
1681 if ((!data
->format_lines
[i
][data
->curr_subline
[i
]]) ||
1682 (data
->curr_subline
[i
] == WPS_MAX_SUBLINES
))
1684 if (data
->curr_subline
[i
] == 1)
1685 only_one_subline
= true;
1686 data
->curr_subline
[i
] = 0;
1689 /* if back where we started after search or
1690 only one subline is defined on the line */
1691 if (((search
> 0) && (data
->curr_subline
[i
] == search_start
)) ||
1694 /* no other subline with a time > 0 exists */
1695 data
->subline_expire_time
[i
] = current_tick
+ 100 * HZ
;
1700 /* get initial time multiplier and
1701 line type flags for this subline */
1702 format_display(gwps
, buf
, sizeof(buf
),
1703 state
->id3
, state
->nid3
,
1704 data
->format_lines
[i
][data
->curr_subline
[i
]],
1705 &data
->format_align
[i
][data
->curr_subline
[i
]],
1706 &data
->time_mult
[i
][data
->curr_subline
[i
]],
1707 &data
->line_type
[i
][data
->curr_subline
[i
]]);
1709 /* only use this subline if subline time > 0 */
1710 if (data
->time_mult
[i
][data
->curr_subline
[i
]] > 0)
1712 new_subline_refresh
= true;
1713 data
->subline_expire_time
[i
] = current_tick
+
1714 BASE_SUBLINE_TIME
* data
->time_mult
[i
][data
->curr_subline
[i
]];
1722 update_line
= false;
1724 if ( !data
->format_lines
[i
][data
->curr_subline
[i
]] )
1727 if ((data
->line_type
[i
][data
->curr_subline
[i
]] & refresh_mode
) ||
1728 (refresh_mode
== WPS_REFRESH_ALL
) ||
1729 new_subline_refresh
)
1732 #ifdef HAVE_LCD_BITMAP
1733 int left_width
, left_xpos
;
1734 int center_width
, center_xpos
;
1735 int right_width
, right_xpos
;
1741 format_display(gwps
, buf
, sizeof(buf
),
1742 state
->id3
, state
->nid3
,
1743 data
->format_lines
[i
][data
->curr_subline
[i
]],
1744 &data
->format_align
[i
][data
->curr_subline
[i
]],
1745 &data
->time_mult
[i
][data
->curr_subline
[i
]],
1747 data
->line_type
[i
][data
->curr_subline
[i
]] = flags
;
1749 #ifdef HAVE_LCD_BITMAP
1751 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1753 int sb_y
= i
*h
+ offset
+ ((h
> data
->progress_height
+ 1)
1754 ? (h
- data
->progress_height
) / 2 : 1);
1756 if (!data
->progress_end
)
1757 data
->progress_end
=display
->width
;
1759 if (gwps
->data
->progressbar
.have_bitmap_pb
)
1760 gui_bitmap_scrollbar_draw(display
, data
->progressbar
.bm
,
1761 data
->progress_start
, sb_y
,
1762 data
->progress_end
-data
->progress_start
,
1763 data
->progressbar
.bm
.height
,
1764 state
->id3
->length
?state
->id3
->length
:1, 0,
1765 state
->id3
->length
?state
->id3
->elapsed
+ state
->ff_rewind_count
:0,
1768 gui_scrollbar_draw(display
, data
->progress_start
, sb_y
,
1769 data
->progress_end
-data
->progress_start
,
1770 data
->progress_height
,
1771 state
->id3
->length
?state
->id3
->length
:1, 0,
1772 state
->id3
->length
?state
->id3
->elapsed
+ state
->ff_rewind_count
:0,
1774 #ifdef AB_REPEAT_ENABLE
1775 if ( ab_repeat_mode_enabled() )
1776 ab_draw_markers(display
, state
->id3
->length
, 0, sb_y
,
1777 data
->progress_height
);
1781 if (flags
& refresh_mode
& WPS_REFRESH_PEAK_METER
) {
1786 peak_meter_y
= i
* h
+ offset
;
1788 /* The user might decide to have the peak meter in the last
1789 line so that it is only displayed if no status bar is
1790 visible. If so we neither want do draw nor enable the
1792 if (peak_meter_y
+ h
<= display
->height
) {
1793 /* found a line with a peak meter -> remember that we must
1796 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
1797 MIN(h
, display
->height
- peak_meter_y
));
1802 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
) {
1803 if (data
->full_line_progressbar
)
1804 draw_player_fullbar(gwps
, buf
, sizeof(buf
));
1806 draw_player_progress(gwps
);
1809 #ifdef HAVE_LCD_BITMAP
1810 /* calculate different string sizes and positions */
1811 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
1812 if (data
->format_align
[i
][data
->curr_subline
[i
]].left
!= 0) {
1813 display
->getstringsize((unsigned char *)data
->format_align
[i
]
1814 [data
->curr_subline
[i
]].left
,
1815 &left_width
, &string_height
);
1822 if (data
->format_align
[i
][data
->curr_subline
[i
]].center
!= 0) {
1823 display
->getstringsize((unsigned char *)data
->format_align
[i
]
1824 [data
->curr_subline
[i
]].center
,
1825 ¢er_width
, &string_height
);
1830 center_xpos
=(display
->width
- center_width
) / 2;
1832 if (data
->format_align
[i
][data
->curr_subline
[i
]].right
!= 0) {
1833 display
->getstringsize((unsigned char *)data
->format_align
[i
]
1834 [data
->curr_subline
[i
]].right
,
1835 &right_width
, &string_height
);
1840 right_xpos
= (display
->width
- right_width
);
1842 /* Checks for overlapping strings.
1843 If needed the overlapping strings will be merged, separated by a
1846 /* CASE 1: left and centered string overlap */
1847 /* there is a left string, need to merge left and center */
1848 if ((left_width
!= 0 && center_width
!= 0) &&
1849 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
1850 /* replace the former separator '\0' of left and
1851 center string with a space */
1852 *(--data
->format_align
[i
][data
->curr_subline
[i
]].center
) = ' ';
1853 /* calculate the new width and position of the merged string */
1854 left_width
= left_width
+ space_width
+ center_width
;
1856 /* there is no centered string anymore */
1859 /* there is no left string, move center to left */
1860 if ((left_width
== 0 && center_width
!= 0) &&
1861 (left_xpos
+ left_width
> center_xpos
)) {
1862 /* move the center string to the left string */
1863 data
->format_align
[i
][data
->curr_subline
[i
]].left
=
1864 data
->format_align
[i
][data
->curr_subline
[i
]].center
;
1865 /* calculate the new width and position of the string */
1866 left_width
= center_width
;
1868 /* there is no centered string anymore */
1872 /* CASE 2: centered and right string overlap */
1873 /* there is a right string, need to merge center and right */
1874 if ((center_width
!= 0 && right_width
!= 0) &&
1875 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
1876 /* replace the former separator '\0' of center and
1877 right string with a space */
1878 *(--data
->format_align
[i
][data
->curr_subline
[i
]].right
) = ' ';
1879 /* move the center string to the right after merge */
1880 data
->format_align
[i
][data
->curr_subline
[i
]].right
=
1881 data
->format_align
[i
][data
->curr_subline
[i
]].center
;
1882 /* calculate the new width and position of the merged string */
1883 right_width
= center_width
+ space_width
+ right_width
;
1884 right_xpos
= (display
->width
- right_width
);
1885 /* there is no centered string anymore */
1888 /* there is no right string, move center to right */
1889 if ((center_width
!= 0 && right_width
== 0) &&
1890 (center_xpos
+ center_width
> right_xpos
)) {
1891 /* move the center string to the right string */
1892 data
->format_align
[i
][data
->curr_subline
[i
]].right
=
1893 data
->format_align
[i
][data
->curr_subline
[i
]].center
;
1894 /* calculate the new width and position of the string */
1895 right_width
= center_width
;
1896 right_xpos
= (display
->width
- right_width
);
1897 /* there is no centered string anymore */
1901 /* CASE 3: left and right overlap
1902 There is no center string anymore, either there never
1903 was one or it has been merged in case 1 or 2 */
1904 /* there is a left string, need to merge left and right */
1905 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
1906 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
1907 /* replace the former separator '\0' of left and
1908 right string with a space */
1909 *(--data
->format_align
[i
][data
->curr_subline
[i
]].right
) = ' ';
1910 /* calculate the new width and position of the string */
1911 left_width
= left_width
+ space_width
+ right_width
;
1913 /* there is no right string anymore */
1916 /* there is no left string, move right to left */
1917 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1918 (left_xpos
+ left_width
> right_xpos
)) {
1919 /* move the right string to the left string */
1920 data
->format_align
[i
][data
->curr_subline
[i
]].left
=
1921 data
->format_align
[i
][data
->curr_subline
[i
]].right
;
1922 /* calculate the new width and position of the string */
1923 left_width
= right_width
;
1925 /* there is no right string anymore */
1931 if (flags
& WPS_REFRESH_SCROLL
) {
1934 if ((refresh_mode
& WPS_REFRESH_SCROLL
) ||
1935 new_subline_refresh
) {
1936 #ifdef HAVE_LCD_BITMAP
1937 ypos
= (i
*string_height
)+display
->getymargin();
1940 if (left_width
>display
->width
) {
1941 display
->puts_scroll(0, i
,
1942 (unsigned char *)data
->format_align
[i
]
1943 [data
->curr_subline
[i
]].left
);
1945 /* clear the line first */
1946 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1947 display
->fillrect(0, ypos
, display
->width
, string_height
);
1948 display
->set_drawmode(DRMODE_SOLID
);
1950 /* Nasty hack: we output an empty scrolling string,
1951 which will reset the scroller for that line */
1952 display
->puts_scroll(0, i
, (unsigned char *)"");
1954 /* print aligned strings */
1955 if (left_width
!= 0)
1957 display
->putsxy(left_xpos
, ypos
,
1958 (unsigned char *)data
->format_align
[i
]
1959 [data
->curr_subline
[i
]].left
);
1961 if (center_width
!= 0)
1963 display
->putsxy(center_xpos
, ypos
,
1964 (unsigned char *)data
->format_align
[i
]
1965 [data
->curr_subline
[i
]].center
);
1967 if (right_width
!= 0)
1969 display
->putsxy(right_xpos
, ypos
,
1970 (unsigned char *)data
->format_align
[i
]
1971 [data
->curr_subline
[i
]].right
);
1975 display
->puts_scroll(0, i
, buf
);
1980 else if (flags
& (WPS_REFRESH_DYNAMIC
| WPS_REFRESH_STATIC
))
1982 /* dynamic / static line */
1983 if ((refresh_mode
& (WPS_REFRESH_DYNAMIC
|WPS_REFRESH_STATIC
)) ||
1984 new_subline_refresh
)
1986 #ifdef HAVE_LCD_BITMAP
1987 ypos
= (i
*string_height
)+display
->getymargin();
1990 /* clear the line first */
1991 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1992 display
->fillrect(0, ypos
, display
->width
, string_height
);
1993 display
->set_drawmode(DRMODE_SOLID
);
1995 /* Nasty hack: we output an empty scrolling string,
1996 which will reset the scroller for that line */
1997 display
->puts_scroll(0, i
, (unsigned char *)"");
1999 /* print aligned strings */
2000 if (left_width
!= 0)
2002 display
->putsxy(left_xpos
, ypos
,
2003 (unsigned char *)data
->format_align
[i
]
2004 [data
->curr_subline
[i
]].left
);
2006 if (center_width
!= 0)
2008 display
->putsxy(center_xpos
, ypos
,
2009 (unsigned char *)data
->format_align
[i
]
2010 [data
->curr_subline
[i
]].center
);
2012 if (right_width
!= 0)
2014 display
->putsxy(right_xpos
, ypos
,
2015 (unsigned char *)data
->format_align
[i
]
2016 [data
->curr_subline
[i
]].right
);
2020 display
->puts(0, i
, buf
);
2025 #ifdef HAVE_LCD_BITMAP
2027 wps_display_images(gwps
,false);
2032 #ifdef HAVE_LCD_BITMAP
2033 /* Display all images */
2034 wps_display_images(gwps
,true);
2036 /* Now we know wether the peak meter is used.
2037 So we can enable / disable the peak meter thread */
2038 data
->peak_meter_enabled
= enable_pm
;
2041 #ifdef CONFIG_BACKLIGHT
2042 if (global_settings
.caption_backlight
&& state
->id3
) {
2043 /* turn on backlight n seconds before track ends, and turn it off n
2044 seconds into the new track. n == backlight_timeout, or 5s */
2045 int n
= backlight_timeout_value
[global_settings
.backlight_timeout
]
2049 n
= 5000; /* use 5s if backlight is always on or off */
2051 if ((state
->id3
->elapsed
< 1000) ||
2052 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
))
2056 #ifdef HAVE_REMOTE_LCD
2057 if (global_settings
.remote_caption_backlight
&& state
->id3
) {
2058 /* turn on remote backlight n seconds before track ends, and turn it
2059 off n seconds into the new track. n == remote_backlight_timeout,
2061 int n
= backlight_timeout_value
[global_settings
.remote_backlight_timeout
]
2065 n
= 5000; /* use 5s if backlight is always on or off */
2067 if ((state
->id3
->elapsed
< 1000) ||
2068 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
))
2069 remote_backlight_on();
2075 #ifdef HAVE_LCD_CHARCELLS
2076 static bool draw_player_progress(struct gui_wps
*gwps
)
2078 char player_progressbar
[7];
2082 struct wps_state
*state
= gwps
->state
;
2083 struct screen
*display
= gwps
->display
;
2087 memset(binline
, 1, sizeof binline
);
2088 memset(player_progressbar
, 1, sizeof player_progressbar
);
2090 if(state
->id3
->elapsed
>= state
->id3
->length
)
2094 if(state
->wps_time_countup
== false)
2095 songpos
= ((state
->id3
->elapsed
- state
->ff_rewind_count
) * 36) /
2098 songpos
= ((state
->id3
->elapsed
+ state
->ff_rewind_count
) * 36) /
2101 for (i
=0; i
< songpos
; i
++)
2104 for (i
=0; i
<=6; i
++) {
2106 player_progressbar
[i
] <<= 1;
2107 player_progressbar
[i
] += binline
[i
*5+j
];
2110 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], player_progressbar
);
2114 static char map_fullbar_char(char ascii_val
)
2116 if (ascii_val
>= '0' && ascii_val
<= '9') {
2117 return(ascii_val
- '0');
2119 else if (ascii_val
== ':') {
2123 return(11); /* anything besides a number or ':' is mapped to <blank> */
2126 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
2128 int i
,j
,lcd_char_pos
;
2130 char player_progressbar
[7];
2132 static const char numbers
[12][4][3]={
2133 {{1,1,1},{1,0,1},{1,0,1},{1,1,1}},/*0*/
2134 {{0,1,0},{1,1,0},{0,1,0},{0,1,0}},/*1*/
2135 {{1,1,1},{0,0,1},{0,1,0},{1,1,1}},/*2*/
2136 {{1,1,1},{0,0,1},{0,1,1},{1,1,1}},/*3*/
2137 {{1,0,0},{1,1,0},{1,1,1},{0,1,0}},/*4*/
2138 {{1,1,1},{1,1,0},{0,0,1},{1,1,0}},/*5*/
2139 {{1,1,1},{1,0,0},{1,1,1},{1,1,1}},/*6*/
2140 {{1,1,1},{0,0,1},{0,1,0},{1,0,0}},/*7*/
2141 {{1,1,1},{1,1,1},{1,0,1},{1,1,1}},/*8*/
2142 {{1,1,1},{1,1,1},{0,0,1},{1,1,1}},/*9*/
2143 {{0,0,0},{0,1,0},{0,0,0},{0,1,0}},/*:*/
2144 {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} /*<blank>*/
2152 struct wps_state
*state
= gwps
->state
;
2153 struct screen
*display
= gwps
->display
;
2154 struct wps_data
*data
= gwps
->data
;
2156 for (i
=0; i
< buf_size
; i
++)
2159 if(state
->id3
->elapsed
>= state
->id3
->length
)
2162 if(state
->wps_time_countup
== false)
2163 songpos
= ((state
->id3
->elapsed
- state
->ff_rewind_count
) * 55) /
2166 songpos
= ((state
->id3
->elapsed
+ state
->ff_rewind_count
) * 55) /
2170 time
=(state
->id3
->elapsed
+ state
->ff_rewind_count
);
2172 memset(timestr
, 0, sizeof(timestr
));
2173 gui_wps_format_time(timestr
, sizeof(timestr
), time
);
2174 for(lcd_char_pos
=0; lcd_char_pos
<6; lcd_char_pos
++) {
2175 digits
[lcd_char_pos
] = map_fullbar_char(timestr
[lcd_char_pos
]);
2178 /* build the progressbar-icons */
2179 for (lcd_char_pos
=0; lcd_char_pos
<6; lcd_char_pos
++) {
2180 memset(binline
, 0, sizeof binline
);
2181 memset(player_progressbar
, 0, sizeof player_progressbar
);
2183 /* make the character (progressbar & digit)*/
2184 for (i
=0; i
<7; i
++) {
2186 /* make the progressbar */
2187 if (lcd_char_pos
==(songpos
/5)) {
2189 if ((j
<(songpos
%5))&&(i
>4))
2195 if (lcd_char_pos
<(songpos
/5)) {
2196 /* full character */
2201 /* insert the digit */
2203 if (numbers
[digits
[lcd_char_pos
]][i
][j
]==1)
2209 for (i
=0; i
<=6; i
++) {
2211 player_progressbar
[i
] <<= 1;
2212 player_progressbar
[i
] += binline
[i
*5+j
];
2216 display
->define_pattern(data
->wps_progress_pat
[lcd_char_pos
+1],
2217 player_progressbar
);
2218 buf
[lcd_char_pos
]=data
->wps_progress_pat
[lcd_char_pos
+1];
2222 /* make rest of the progressbar if necessary */
2225 /* set the characters positions that use the full 5 pixel wide bar */
2226 for (lcd_char_pos
=6; lcd_char_pos
< (songpos
/5); lcd_char_pos
++)
2227 buf
[lcd_char_pos
] = 0x86; /* '_' */
2229 /* build the partial bar character for the tail character position */
2230 memset(binline
, 0, sizeof binline
);
2231 memset(player_progressbar
, 0, sizeof player_progressbar
);
2233 for (i
=5; i
<7; i
++) {
2235 if (j
<(songpos
%5)) {
2241 for (i
=0; i
<7; i
++) {
2243 player_progressbar
[i
] <<= 1;
2244 player_progressbar
[i
] += binline
[i
*5+j
];
2248 display
->define_pattern(data
->wps_progress_pat
[7],player_progressbar
);
2250 buf
[songpos
/5]=data
->wps_progress_pat
[7];
2258 if (global_settings
.volume
< sound_min(SOUND_VOLUME
))
2259 global_settings
.volume
= sound_min(SOUND_VOLUME
);
2260 if (global_settings
.volume
> sound_max(SOUND_VOLUME
))
2261 global_settings
.volume
= sound_max(SOUND_VOLUME
);
2262 sound_set_volume(global_settings
.volume
);
2265 /* return true if screen restore is needed
2266 return false otherwise
2268 bool update_onvol_change(struct gui_wps
* gwps
)
2270 gui_wps_statusbar_draw(gwps
, false);
2271 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
2273 #ifdef HAVE_LCD_CHARCELLS
2274 gui_splash(gwps
->display
,0, false, "Vol: %d dB ",
2275 sound_val2phys(SOUND_VOLUME
, global_settings
.volume
));
2281 bool ffwd_rew(int button
)
2283 static const int ff_rew_steps
[] = {
2284 1000, 2000, 3000, 4000,
2285 5000, 6000, 8000, 10000,
2286 15000, 20000, 25000, 30000,
2290 unsigned int step
= 0; /* current ff/rewind step */
2291 unsigned int max_step
= 0; /* maximum ff/rewind step */
2292 int ff_rewind_count
= 0; /* current ff/rewind count (in ticks) */
2293 int direction
= -1; /* forward=1 or backward=-1 */
2294 long accel_tick
= 0; /* next time at which to bump the step size */
2312 if (wps_state
.ff_rewind
)
2316 /* fast forwarding, calc max step relative to end */
2317 max_step
= (wps_state
.id3
->length
-
2318 (wps_state
.id3
->elapsed
+
2320 FF_REWIND_MAX_PERCENT
/ 100;
2324 /* rewinding, calc max step relative to start */
2325 max_step
= (wps_state
.id3
->elapsed
+ ff_rewind_count
) *
2326 FF_REWIND_MAX_PERCENT
/ 100;
2329 max_step
= MAX(max_step
, MIN_FF_REWIND_STEP
);
2331 if (step
> max_step
)
2334 ff_rewind_count
+= step
* direction
;
2336 if (global_settings
.ff_rewind_accel
!= 0 &&
2337 current_tick
>= accel_tick
)
2340 accel_tick
= current_tick
+
2341 global_settings
.ff_rewind_accel
*HZ
;
2346 if ( (audio_status() & AUDIO_STATUS_PLAY
) &&
2347 wps_state
.id3
&& wps_state
.id3
->length
)
2349 if (!wps_state
.paused
)
2350 #if (CONFIG_CODEC == SWCODEC)
2351 audio_pre_ff_rewind();
2355 #if CONFIG_KEYPAD == PLAYER_PAD
2357 gui_wps
[i
].display
->stop_scroll();
2360 status_set_ffmode(STATUS_FASTFORWARD
);
2362 status_set_ffmode(STATUS_FASTBACKWARD
);
2364 wps_state
.ff_rewind
= true;
2366 step
= ff_rew_steps
[global_settings
.ff_rewind_min_step
];
2368 accel_tick
= current_tick
+
2369 global_settings
.ff_rewind_accel
*HZ
;
2375 if (direction
> 0) {
2376 if ((wps_state
.id3
->elapsed
+ ff_rewind_count
) >
2377 wps_state
.id3
->length
)
2378 ff_rewind_count
= wps_state
.id3
->length
-
2379 wps_state
.id3
->elapsed
;
2382 if ((int)(wps_state
.id3
->elapsed
+ ff_rewind_count
) < 0)
2383 ff_rewind_count
= -wps_state
.id3
->elapsed
;
2387 gui_wps_refresh(&gui_wps
[i
],
2388 (wps_state
.wps_time_countup
== false)?
2389 ff_rewind_count
:-ff_rewind_count
,
2390 WPS_REFRESH_PLAYER_PROGRESS
|
2391 WPS_REFRESH_DYNAMIC
);
2401 wps_state
.id3
->elapsed
= wps_state
.id3
->elapsed
+ff_rewind_count
;
2402 audio_ff_rewind(wps_state
.id3
->elapsed
);
2403 ff_rewind_count
= 0;
2404 wps_state
.ff_rewind
= false;
2405 status_set_ffmode(0);
2406 #if (CONFIG_CODEC != SWCODEC)
2407 if (!wps_state
.paused
)
2410 #ifdef HAVE_LCD_CHARCELLS
2417 if(default_event_handler(button
) == SYS_USB_CONNECTED
) {
2418 status_set_ffmode(0);
2425 button
= button_get(true);
2431 bool gui_wps_display(void)
2434 if (!wps_state
.id3
&& !(audio_status() & AUDIO_STATUS_PLAY
))
2436 global_settings
.resume_index
= -1;
2437 #ifdef HAVE_LCD_CHARCELLS
2438 gui_syncsplash(HZ
, true, str(LANG_END_PLAYLIST_PLAYER
));
2440 gui_syncstatusbar_draw(&statusbars
, true);
2441 gui_syncsplash(HZ
, true, str(LANG_END_PLAYLIST_RECORDER
));
2449 gui_wps
[i
].display
->clear_display();
2450 if (!gui_wps
[i
].data
->wps_loaded
) {
2451 if ( !gui_wps
[i
].data
->format_buffer
[0] ) {
2452 /* set the default wps for the main-screen */
2455 #ifdef HAVE_LCD_BITMAP
2456 wps_data_load(gui_wps
[i
].data
,
2457 "%s%?it<%?in<%in. |>%it|%fn>\n"
2458 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
2459 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
2461 "%al%pc/%pt%ar[%pp:%pe]\n"
2462 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
2466 wps_data_load(gui_wps
[i
].data
,
2467 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
2468 "%pc%?ps<*|/>%pt\n", false);
2472 /* set the default wps for the remote-screen */
2475 wps_data_load(gui_wps
[i
].data
,
2476 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
2477 "%s%?it<%?in<%in. |>%it|%fn>\n"
2478 "%al%pc/%pt%ar[%pp:%pe]\n"
2479 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
2490 gui_wps_refresh(&gui_wps
[i
], 0, WPS_REFRESH_ALL
);
2495 bool update(struct gui_wps
*gwps
)
2497 bool track_changed
= audio_has_changed_track();
2498 bool retcode
= false;
2500 gwps
->state
->nid3
= audio_next_track();
2503 gwps
->display
->stop_scroll();
2504 gwps
->state
->id3
= audio_current_track();
2505 if (gui_wps_display())
2508 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
2511 if (gwps
->state
->id3
)
2512 memcpy(gwps
->state
->current_track_path
, gwps
->state
->id3
->path
,
2513 sizeof(gwps
->state
->current_track_path
));
2516 if (gwps
->state
->id3
)
2517 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
2519 gui_wps_statusbar_draw(gwps
, false);
2525 void display_keylock_text(bool locked
)
2530 gui_wps
[i
].display
->stop_scroll();
2532 #ifdef HAVE_LCD_CHARCELLS
2534 s
= str(LANG_KEYLOCK_ON_PLAYER
);
2536 s
= str(LANG_KEYLOCK_OFF_PLAYER
);
2539 s
= str(LANG_KEYLOCK_ON_RECORDER
);
2541 s
= str(LANG_KEYLOCK_OFF_RECORDER
);
2543 gui_syncsplash(HZ
, true, s
);
2546 void waitfor_nokey(void)
2548 /* wait until all keys are released */
2549 while (button_get(false) != BUTTON_NONE
)