1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002-2007 Björn Stenberg
11 * Copyright (C) 2007-2008 Nicolas Pennequin
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
29 #include "settings_list.h"
30 #include "rbunicode.h"
31 #include "timefuncs.h"
35 #include "powermgmt.h"
38 #ifdef HAVE_LCD_CHARCELLS
42 #include "mp3_playback.h"
46 #ifdef HAVE_LCD_BITMAP
52 #if CONFIG_CODEC == SWCODEC
57 #include "wps_internals.h"
59 #include "root_menu.h"
61 #include "recording.h"
64 static char* get_codectype(const struct mp3entry
* id3
)
66 if (id3
&& id3
->codectype
< AFMT_NUM_CODECS
) {
67 return (char*)audio_formats
[id3
->codectype
].label
;
73 /* Extract a part from a path.
75 * buf - buffer extract part to.
76 * buf_size - size of buffer.
77 * path - path to extract from.
78 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
79 * parent of parent, etc.
81 * Returns buf if the desired level was found, NULL otherwise.
83 static char* get_dir(char* buf
, int buf_size
, const char* path
, int level
)
89 sep
= path
+ strlen(path
);
104 if (level
|| (last_sep
<= sep
))
107 len
= MIN(last_sep
- sep
, buf_size
- 1);
108 strlcpy(buf
, sep
+ 1, len
+ 1);
112 /* Return the tag found at index i and write its value in buf.
113 The return value is buf if the tag had a value, or NULL if not.
115 intval is used with conditionals/enums: when this function is called,
116 intval should contain the number of options in the conditional/enum.
117 When this function returns, intval is -1 if the tag is non numeric or,
118 if the tag is numeric, *intval is the enum case we want to go to (between 1
119 and the original value of *intval, inclusive).
120 When not treating a conditional/enum, intval should be NULL.
123 /* a few convinience macros for the id3 == NULL case
124 * depends on a few variable names in get_token_value() */
126 #define HANDLE_NULL_ID3(id3field) (LIKELY(id3) ? (id3field) : NULL)
128 #define HANDLE_NULL_ID3_NUM_ZERO { if (UNLIKELY(!id3)) return zero_str; }
130 #define HANDLE_NULL_ID3_NUM_INTVAL(id3field) \
133 *intval = (LIKELY(id3) ? (id3field) + 1 : 0); \
137 snprintf(buf, buf_size, "%ld", (id3field)); \
143 const char *get_token_value(struct gui_wps
*gwps
,
144 struct wps_token
*token
,
145 char *buf
, int buf_size
,
151 struct wps_data
*data
= gwps
->data
;
152 struct wps_state
*state
= gwps
->state
;
154 static const char * const zero_str
= "0";
159 struct mp3entry
*id3
;
168 elapsed
= id3
->elapsed
;
169 length
= id3
->length
;
178 struct tm
* tm
= NULL
;
180 /* if the token is an RTC one, update the time
181 and do the necessary checks */
182 if (token
->type
>= WPS_TOKENS_RTC_BEGIN
183 && token
->type
<= WPS_TOKENS_RTC_END
)
201 case WPS_TOKEN_CHARACTER
:
202 if (token
->value
.c
== '\n')
204 return &(token
->value
.c
);
206 case WPS_TOKEN_STRING
:
207 return (char*)token
->value
.data
;
209 case WPS_TOKEN_TRANSLATEDSTRING
:
210 return (char*)P2STR(ID2P(token
->value
.i
));
212 case WPS_TOKEN_TRACK_TIME_ELAPSED
:
213 format_time(buf
, buf_size
,
214 elapsed
+ state
->ff_rewind_count
);
217 case WPS_TOKEN_TRACK_TIME_REMAINING
:
218 format_time(buf
, buf_size
,
220 state
->ff_rewind_count
);
223 case WPS_TOKEN_TRACK_LENGTH
:
224 format_time(buf
, buf_size
, length
);
227 case WPS_TOKEN_PLAYLIST_ENTRIES
:
228 snprintf(buf
, buf_size
, "%d", playlist_amount());
231 case WPS_TOKEN_PLAYLIST_NAME
:
232 return playlist_name(NULL
, buf
, buf_size
);
234 case WPS_TOKEN_PLAYLIST_POSITION
:
235 snprintf(buf
, buf_size
, "%d", playlist_get_display_index());
238 case WPS_TOKEN_PLAYLIST_SHUFFLE
:
239 if ( global_settings
.playlist_shuffle
)
245 case WPS_TOKEN_VOLUME
:
246 snprintf(buf
, buf_size
, "%d", global_settings
.volume
);
249 if (global_settings
.volume
== sound_min(SOUND_VOLUME
))
253 else if (global_settings
.volume
== 0)
257 else if (global_settings
.volume
> 0)
263 *intval
= (limit
- 3) * (global_settings
.volume
264 - sound_min(SOUND_VOLUME
) - 1)
265 / (-1 - sound_min(SOUND_VOLUME
)) + 2;
270 case WPS_TOKEN_TRACK_ELAPSED_PERCENT
:
276 *intval
= limit
* (elapsed
+ state
->ff_rewind_count
)
279 snprintf(buf
, buf_size
, "%d",
280 100*(elapsed
+ state
->ff_rewind_count
) / length
);
283 case WPS_TOKEN_METADATA_ARTIST
:
284 return HANDLE_NULL_ID3(id3
->artist
);
286 case WPS_TOKEN_METADATA_COMPOSER
:
287 return HANDLE_NULL_ID3(id3
->composer
);
289 case WPS_TOKEN_METADATA_ALBUM
:
290 return HANDLE_NULL_ID3(id3
->album
);
292 case WPS_TOKEN_METADATA_ALBUM_ARTIST
:
293 return HANDLE_NULL_ID3(id3
->albumartist
);
295 case WPS_TOKEN_METADATA_GROUPING
:
296 return HANDLE_NULL_ID3(id3
->grouping
);
298 case WPS_TOKEN_METADATA_GENRE
:
299 return HANDLE_NULL_ID3(id3
->genre_string
);
301 case WPS_TOKEN_METADATA_DISC_NUMBER
:
303 if (id3
->disc_string
)
304 return id3
->disc_string
;
306 snprintf(buf
, buf_size
, "%d", id3
->discnum
);
312 case WPS_TOKEN_METADATA_TRACK_NUMBER
:
314 if (id3
->track_string
)
315 return id3
->track_string
;
318 snprintf(buf
, buf_size
, "%d", id3
->tracknum
);
324 case WPS_TOKEN_METADATA_TRACK_TITLE
:
325 return HANDLE_NULL_ID3(id3
->title
);
327 case WPS_TOKEN_METADATA_VERSION
:
330 switch (id3
->id3version
)
353 case WPS_TOKEN_METADATA_YEAR
:
355 if( id3
->year_string
)
356 return id3
->year_string
;
359 snprintf(buf
, buf_size
, "%d", id3
->year
);
365 case WPS_TOKEN_METADATA_COMMENT
:
366 return HANDLE_NULL_ID3(id3
->comment
);
369 case WPS_TOKEN_ALBUMART_FOUND
:
370 if (data
->albumart
) {
371 if (playback_current_aa_hid(data
->playback_aa_slot
) >= 0)
376 case WPS_TOKEN_ALBUMART_DISPLAY
:
379 if (!data
->albumart
->draw
)
380 data
->albumart
->draw
= true;
384 case WPS_TOKEN_FILE_BITRATE
:
385 if(id3
&& id3
->bitrate
)
386 snprintf(buf
, buf_size
, "%d", id3
->bitrate
);
391 case WPS_TOKEN_FILE_CODEC
:
396 else if(id3
->codectype
== AFMT_UNKNOWN
)
397 *intval
= AFMT_NUM_CODECS
;
399 *intval
= id3
->codectype
;
401 return get_codectype(id3
);
403 case WPS_TOKEN_FILE_FREQUENCY
:
404 HANDLE_NULL_ID3_NUM_ZERO
;
405 snprintf(buf
, buf_size
, "%ld", id3
->frequency
);
408 case WPS_TOKEN_FILE_FREQUENCY_KHZ
:
409 HANDLE_NULL_ID3_NUM_ZERO
;
410 /* ignore remainders < 100, so 22050 Hz becomes just 22k */
411 if ((id3
->frequency
% 1000) < 100)
412 snprintf(buf
, buf_size
, "%ld", id3
->frequency
/ 1000);
414 snprintf(buf
, buf_size
, "%ld.%d",
415 id3
->frequency
/ 1000,
416 (id3
->frequency
% 1000) / 100);
419 case WPS_TOKEN_FILE_NAME
:
420 if (LIKELY(id3
) && get_dir(buf
, buf_size
, id3
->path
, 0)) {
421 /* Remove extension */
422 char* sep
= strrchr(buf
, '.');
432 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION
:
434 return get_dir(buf
, buf_size
, id3
->path
, 0);
437 case WPS_TOKEN_FILE_PATH
:
438 return HANDLE_NULL_ID3(id3
->path
);
440 case WPS_TOKEN_FILE_SIZE
:
441 HANDLE_NULL_ID3_NUM_ZERO
;
442 snprintf(buf
, buf_size
, "%ld", id3
->filesize
/ 1024);
445 case WPS_TOKEN_FILE_VBR
:
446 return (LIKELY(id3
) && id3
->vbr
) ? "(avg)" : NULL
;
448 case WPS_TOKEN_FILE_DIRECTORY
:
450 return get_dir(buf
, buf_size
, id3
->path
, token
->value
.i
);
453 case WPS_TOKEN_BATTERY_PERCENT
:
455 int l
= battery_level();
459 limit
= MAX(limit
, 2);
461 /* First enum is used for "unknown level". */
462 *intval
= (limit
- 1) * l
/ 100 + 2;
469 snprintf(buf
, buf_size
, "%d", l
);
476 case WPS_TOKEN_BATTERY_VOLTS
:
478 unsigned int v
= battery_voltage();
479 snprintf(buf
, buf_size
, "%d.%02d", v
/ 1000, (v
% 1000) / 10);
483 case WPS_TOKEN_BATTERY_TIME
:
485 int t
= battery_time();
487 snprintf(buf
, buf_size
, "%dh %dm", t
/ 60, t
% 60);
494 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED
:
496 if(charger_input_state
==CHARGER
)
502 #if CONFIG_CHARGING >= CHARGING_MONITOR
503 case WPS_TOKEN_BATTERY_CHARGING
:
505 if (charge_state
== CHARGING
|| charge_state
== TOPOFF
) {
512 case WPS_TOKEN_BATTERY_SLEEPTIME
:
514 if (get_sleep_timer() == 0)
518 format_time(buf
, buf_size
, get_sleep_timer() * 1000);
523 case WPS_TOKEN_PLAYBACK_STATUS
:
525 int status
= current_playmode();
527 int mode
= 1; /* stop */
528 if (status
== STATUS_PLAY
)
530 if (is_wps_fading() ||
531 (status
== STATUS_PAUSE
&& !status_get_ffmode()))
532 mode
= 3; /* pause */
535 if (status_get_ffmode() == STATUS_FASTFORWARD
)
537 if (status_get_ffmode() == STATUS_FASTBACKWARD
)
540 #ifdef HAVE_RECORDING
542 if (status
== STATUS_RECORD
)
544 else if (status
== STATUS_RECORD_PAUSE
)
549 if (status
== STATUS_RADIO
)
551 else if (status
== STATUS_RADIO_PAUSE
)
559 snprintf(buf
, buf_size
, "%d", mode
-1);
563 case WPS_TOKEN_REPEAT_MODE
:
565 *intval
= global_settings
.repeat_mode
+ 1;
566 snprintf(buf
, buf_size
, "%d", global_settings
.repeat_mode
);
569 case WPS_TOKEN_RTC_PRESENT
:
577 case WPS_TOKEN_RTC_12HOUR_CFG
:
579 *intval
= global_settings
.timeformat
+ 1;
580 snprintf(buf
, buf_size
, "%d", global_settings
.timeformat
);
583 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
584 /* d: day of month (01..31) */
585 snprintf(buf
, buf_size
, "%02d", tm
->tm_mday
);
588 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
589 /* e: day of month, blank padded ( 1..31) */
590 snprintf(buf
, buf_size
, "%2d", tm
->tm_mday
);
593 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
594 /* H: hour (00..23) */
595 snprintf(buf
, buf_size
, "%02d", tm
->tm_hour
);
598 case WPS_TOKEN_RTC_HOUR_24
:
599 /* k: hour ( 0..23) */
600 snprintf(buf
, buf_size
, "%2d", tm
->tm_hour
);
603 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
604 /* I: hour (01..12) */
605 snprintf(buf
, buf_size
, "%02d",
606 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
609 case WPS_TOKEN_RTC_HOUR_12
:
610 /* l: hour ( 1..12) */
611 snprintf(buf
, buf_size
, "%2d",
612 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
615 case WPS_TOKEN_RTC_MONTH
:
616 /* m: month (01..12) */
618 *intval
= tm
->tm_mon
+ 1;
619 snprintf(buf
, buf_size
, "%02d", tm
->tm_mon
+ 1);
622 case WPS_TOKEN_RTC_MINUTE
:
623 /* M: minute (00..59) */
624 snprintf(buf
, buf_size
, "%02d", tm
->tm_min
);
627 case WPS_TOKEN_RTC_SECOND
:
628 /* S: second (00..59) */
629 snprintf(buf
, buf_size
, "%02d", tm
->tm_sec
);
632 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
633 /* y: last two digits of year (00..99) */
634 snprintf(buf
, buf_size
, "%02d", tm
->tm_year
% 100);
637 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
638 /* Y: year (1970...) */
639 snprintf(buf
, buf_size
, "%04d", tm
->tm_year
+ 1900);
642 case WPS_TOKEN_RTC_AM_PM_UPPER
:
643 /* p: upper case AM or PM indicator */
644 return tm
->tm_hour
/12 == 0 ? "AM" : "PM";
646 case WPS_TOKEN_RTC_AM_PM_LOWER
:
647 /* P: lower case am or pm indicator */
648 return tm
->tm_hour
/12 == 0 ? "am" : "pm";
650 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
651 /* a: abbreviated weekday name (Sun..Sat) */
652 return str(LANG_WEEKDAY_SUNDAY
+ tm
->tm_wday
);
654 case WPS_TOKEN_RTC_MONTH_NAME
:
655 /* b: abbreviated month name (Jan..Dec) */
656 return str(LANG_MONTH_JANUARY
+ tm
->tm_mon
);
658 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
659 /* u: day of week (1..7); 1 is Monday */
661 *intval
= (tm
->tm_wday
== 0) ? 7 : tm
->tm_wday
;
662 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
+ 1);
665 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
666 /* w: day of week (0..6); 0 is Sunday */
668 *intval
= tm
->tm_wday
+ 1;
669 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
);
672 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
673 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
674 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
675 case WPS_TOKEN_RTC_HOUR_24
:
676 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
677 case WPS_TOKEN_RTC_HOUR_12
:
678 case WPS_TOKEN_RTC_MONTH
:
679 case WPS_TOKEN_RTC_MINUTE
:
680 case WPS_TOKEN_RTC_SECOND
:
681 case WPS_TOKEN_RTC_AM_PM_UPPER
:
682 case WPS_TOKEN_RTC_AM_PM_LOWER
:
683 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
685 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
687 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
688 case WPS_TOKEN_RTC_MONTH_NAME
:
690 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
691 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
695 #ifdef HAVE_LCD_CHARCELLS
696 case WPS_TOKEN_PROGRESSBAR
:
698 char *end
= utf8encode(data
->wps_progress_pat
[0], buf
);
703 case WPS_TOKEN_PLAYER_PROGRESSBAR
:
706 /* we need 11 characters (full line) for
708 strlcpy(buf
, " ", buf_size
);
712 /* Tell the user if we have an OldPlayer */
713 strlcpy(buf
, " <Old LCD> ", buf_size
);
720 case WPS_TOKEN_DATABASE_PLAYCOUNT
:
721 HANDLE_NULL_ID3_NUM_INTVAL(id3
->playcount
);
723 case WPS_TOKEN_DATABASE_RATING
:
724 HANDLE_NULL_ID3_NUM_INTVAL(id3
->rating
);
726 case WPS_TOKEN_DATABASE_AUTOSCORE
:
727 HANDLE_NULL_ID3_NUM_INTVAL(id3
->score
);
730 #if (CONFIG_CODEC == SWCODEC)
731 case WPS_TOKEN_CROSSFADE
:
733 *intval
= global_settings
.crossfade
+ 1;
734 snprintf(buf
, buf_size
, "%d", global_settings
.crossfade
);
737 case WPS_TOKEN_REPLAYGAIN
:
741 if (global_settings
.replaygain_type
== REPLAYGAIN_OFF
)
747 type
= get_replaygain_mode(id3
->track_gain_string
!= NULL
,
748 id3
->album_gain_string
!= NULL
);
753 val
= 6; /* no tag */
757 if (global_settings
.replaygain_type
== REPLAYGAIN_SHUFFLE
)
770 /* due to above, coming here with !id3 shouldn't be possible */
773 strlcpy(buf
, id3
->track_gain_string
, buf_size
);
777 strlcpy(buf
, id3
->album_gain_string
, buf_size
);
782 #endif /* (CONFIG_CODEC == SWCODEC) */
784 #if (CONFIG_CODEC != MAS3507D)
785 case WPS_TOKEN_SOUND_PITCH
:
787 int32_t pitch
= sound_get_pitch();
788 snprintf(buf
, buf_size
, "%ld.%ld",
789 pitch
/ PITCH_SPEED_PRECISION
,
790 (pitch
% PITCH_SPEED_PRECISION
) / (PITCH_SPEED_PRECISION
/ 10));
795 case WPS_TOKEN_MAIN_HOLD
:
796 #ifdef HAS_BUTTON_HOLD
799 if (is_keys_locked())
800 #endif /*hold switch or softlock*/
805 #ifdef HAS_REMOTE_BUTTON_HOLD
806 case WPS_TOKEN_REMOTE_HOLD
:
807 if (remote_button_hold())
813 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
814 case WPS_TOKEN_VLED_HDD
:
820 case WPS_TOKEN_BUTTON_VOLUME
:
821 if (global_status
.last_volume_change
&&
822 TIME_BEFORE(current_tick
, global_status
.last_volume_change
+
823 token
->value
.i
* TIMEOUT_UNIT
))
826 case WPS_TOKEN_LASTTOUCH
:
827 #ifdef HAVE_TOUCHSCREEN
828 if (TIME_BEFORE(current_tick
, token
->value
.i
* TIMEOUT_UNIT
+
829 touchscreen_last_touch()))
834 case WPS_TOKEN_SETTING
:
838 /* Handle contionals */
839 const struct settings_list
*s
= settings
+token
->value
.i
;
840 switch (s
->flags
&F_T_MASK
)
845 /* %?St|name|<#000000|#000001|...|#FFFFFF> */
846 /* shouldn't overflow since colors are stored
848 * but this is pretty useless anyway */
849 *intval
= *(int*)s
->setting
+ 1;
850 else if (s
->cfg_vals
== NULL
)
851 /* %?St|name|<1st choice|2nd choice|...> */
852 *intval
= (*(int*)s
->setting
-s
->int_setting
->min
)
853 /s
->int_setting
->step
+ 1;
855 /* %?St|name|<1st choice|2nd choice|...> */
856 /* Not sure about this one. cfg_name/vals are
857 * indexed from 0 right? */
858 *intval
= *(int*)s
->setting
+ 1;
861 /* %?St|name|<if true|if false> */
862 *intval
= *(bool*)s
->setting
?1:2;
865 /* %?St|name|<if non empty string|if empty>
866 * The string's emptyness discards the setting's
867 * prefix and suffix */
868 *intval
= ((char*)s
->setting
)[0]?1:2;
871 /* This shouldn't happen ... but you never know */
876 cfg_to_string(token
->value
.i
,buf
,buf_size
);
879 case WPS_TOKEN_CURRENT_SCREEN
:
881 int curr_screen
= current_screen();
883 #ifdef HAVE_RECORDING
884 /* override current_screen() for recording screen since it may
885 * be entered from the radio screen */
886 if (in_recording_screen())
887 curr_screen
= GO_TO_RECSCREEN
;
895 #ifdef HAVE_RECORDING
896 case GO_TO_RECSCREEN
:
912 *intval
= curr_screen
;
914 snprintf(buf
, buf_size
, "%d", curr_screen
);