1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 Jerome Kuptz
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
30 #include "backlight.h"
33 #include "filetypes.h"
38 #include "gwps-common.h"
45 #ifdef HAVE_LCD_BITMAP
47 #include "peakmeter.h"
59 #include "ata_idle_notify.h"
60 #include "root_menu.h"
62 #include "quickscreen.h"
63 #include "pitchscreen.h"
64 #include "appevents.h"
68 #define RESTORE_WPS_INSTANTLY 0l
69 #define RESTORE_WPS_NEXT_SECOND ((long)(HZ+current_tick))
71 #define DEFAULT_SKIP_TRESH 3000ul
74 /* currently only one wps_state is needed */
75 struct wps_state wps_state
;
76 struct gui_wps gui_wps
[NB_SCREENS
];
77 static struct wps_data wps_datas
[NB_SCREENS
];
79 /* initial setup of wps_data */
80 static void wps_state_init(void);
82 static void change_dir(int direction
)
84 if (global_settings
.prevent_skip
)
89 else if (direction
> 0)
93 static void prev_track(unsigned long skip_thresh
)
95 if (!global_settings
.prevent_skip
96 && (wps_state
.id3
->elapsed
< skip_thresh
))
103 if (cuesheet_is_enabled() && wps_state
.id3
->cuesheet_type
)
105 curr_cuesheet_skip(-1, wps_state
.id3
->elapsed
);
109 if (!wps_state
.paused
)
110 #if (CONFIG_CODEC == SWCODEC)
111 audio_pre_ff_rewind();
118 #if (CONFIG_CODEC != SWCODEC)
119 if (!wps_state
.paused
)
125 static void next_track(void)
127 if (global_settings
.prevent_skip
)
129 /* take care of if we're playing a cuesheet */
130 if (cuesheet_is_enabled() && wps_state
.id3
->cuesheet_type
)
132 if (curr_cuesheet_skip(1, wps_state
.id3
->elapsed
))
134 /* if the result was false, then we really want
135 to skip to the next track */
143 static void play_hop(int direction
)
145 unsigned long step
= ((unsigned long)global_settings
.skip_length
)*1000;
146 unsigned long elapsed
= wps_state
.id3
->elapsed
;
147 unsigned long remaining
= wps_state
.id3
->length
- elapsed
;
149 if (!global_settings
.prevent_skip
&&
151 (direction
> 0 && step
>= remaining
) ||
152 (direction
< 0 && elapsed
< DEFAULT_SKIP_TRESH
)))
153 { /* Do normal track skipping */
156 else if (direction
< 0)
157 prev_track(DEFAULT_SKIP_TRESH
);
161 if (direction
== 1 && step
>= remaining
)
163 #if CONFIG_CODEC == SWCODEC
164 if(global_settings
.beep
)
165 pcmbuf_beep(1000, 150, 1500*global_settings
.beep
);
169 else if ((direction
== -1 && elapsed
< step
))
175 elapsed
+= step
* direction
;
177 if((audio_status() & AUDIO_STATUS_PLAY
) && !wps_state
.paused
)
179 #if (CONFIG_CODEC == SWCODEC)
180 audio_pre_ff_rewind();
185 audio_ff_rewind(wps_state
.id3
->elapsed
= elapsed
);
186 #if (CONFIG_CODEC != SWCODEC)
187 if (!wps_state
.paused
)
192 static void gwps_fix_statusbars(void)
194 #ifdef HAVE_LCD_BITMAP
196 wpsbars
= VP_SB_HIDE_ALL
;
200 if (gui_wps
[i
].data
->wps_sb_tag
)
201 draw
= gui_wps
[i
].data
->show_sb_on_wps
;
202 else if (global_settings
.statusbar
)
203 wpsbars
|= VP_SB_ONSCREEN(i
);
205 wpsbars
|= (VP_SB_ONSCREEN(i
) | VP_SB_IGNORE_SETTING(i
));
208 wpsbars
= VP_SB_ALLSCREENS
;
213 static void gwps_leave_wps(void)
215 int i
, oldbars
= VP_SB_HIDE_ALL
;
218 gui_wps
[i
].display
->stop_scroll();
219 if (global_settings
.statusbar
)
220 oldbars
= VP_SB_ALLSCREENS
;
223 show_main_backdrop();
225 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
226 show_remote_main_backdrop();
228 viewportmanager_set_statusbar(oldbars
);
231 void gwps_draw_statusbars(void)
233 viewportmanager_set_statusbar(wpsbars
);
236 /* The WPS can be left in two ways:
237 * a) call a function, which draws over the wps. In this case, the wps
238 * will be still active (i.e. the below function didn't return)
239 * b) return with a value evaluated by root_menu.c, in this case the wps
240 * is really left, and root_menu will handle the next screen
242 * In either way, call gwps_leave_wps(), in order to restore the correct
243 * "main screen" backdrops and statusbars
245 long gui_wps_show(void)
248 bool restore
= false;
249 long restoretimer
= RESTORE_WPS_INSTANTLY
; /* timer to delay screen redraw temporarily */
251 bool bookmark
= false;
252 bool update_track
= false;
254 long last_left
= 0, last_right
= 0;
257 #ifdef HAVE_LCD_CHARCELLS
258 status_set_audio(true);
259 status_set_param(false);
262 gwps_fix_statusbars();
264 #ifdef AB_REPEAT_ENABLE
268 if(audio_status() & AUDIO_STATUS_PLAY
)
270 wps_state
.id3
= audio_current_track();
271 wps_state
.nid3
= audio_next_track();
272 restore
= true; /* force initial full redraw */
277 bool audio_paused
= (audio_status() & AUDIO_STATUS_PAUSE
)?true:false;
279 /* did someone else (i.e power thread) change audio pause mode? */
280 if (wps_state
.paused
!= audio_paused
) {
281 wps_state
.paused
= audio_paused
;
283 /* if another thread paused audio, we are probably in car mode,
284 about to shut down. lets save the settings. */
285 if (wps_state
.paused
) {
287 #if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
288 call_storage_idle_notifys(true);
293 #ifdef HAVE_LCD_BITMAP
294 /* when the peak meter is enabled we want to have a
295 few extra updates to make it look smooth. On the
296 other hand we don't want to waste energy if it
301 if(gui_wps
[i
].data
->peak_meter_enabled
)
306 long next_refresh
= current_tick
;
307 long next_big_refresh
= current_tick
+ HZ
/ 5;
308 button
= BUTTON_NONE
;
309 while (TIME_BEFORE(current_tick
, next_big_refresh
)) {
310 button
= get_action(CONTEXT_WPS
|ALLOW_SOFTLOCK
,TIMEOUT_NOBLOCK
);
311 if (button
!= ACTION_NONE
) {
315 sleep(0); /* Sleep until end of current tick. */
317 if (TIME_AFTER(current_tick
, next_refresh
)) {
320 if(gui_wps
[i
].data
->peak_meter_enabled
)
321 gui_wps_redraw(&gui_wps
[i
], 0,
322 WPS_REFRESH_PEAK_METER
);
323 next_refresh
+= HZ
/ PEAK_METER_FPS
;
330 /* The peak meter is disabled
331 -> no additional screen updates needed */
335 button
= get_action(CONTEXT_WPS
|ALLOW_SOFTLOCK
,HZ
/5);
338 /* Exit if audio has stopped playing. This happens e.g. at end of
339 playlist or if using the sleep timer. */
340 if (!(audio_status() & AUDIO_STATUS_PLAY
))
342 /* The iPods/X5/M5 use a single button for the A-B mode markers,
343 defined as ACTION_WPSAB_SINGLE in their config files. */
344 #ifdef ACTION_WPSAB_SINGLE
345 if (!global_settings
.party_mode
&& ab_repeat_mode_enabled())
347 static int wps_ab_state
= 0;
348 if (button
== ACTION_WPSAB_SINGLE
)
350 switch (wps_ab_state
)
352 case 0: /* set the A spot */
353 button
= ACTION_WPS_ABSETA_PREVDIR
;
355 case 1: /* set the B spot */
356 button
= ACTION_WPS_ABSETB_NEXTDIR
;
359 button
= ACTION_WPS_ABRESET
;
362 wps_ab_state
= (wps_ab_state
+1) % 3;
368 case ACTION_WPS_CONTEXT
:
371 /* if music is stopped in the context menu we want to exit the wps */
372 if (onplay(wps_state
.id3
->path
,
373 FILE_ATTR_AUDIO
, CONTEXT_WPS
) == ONPLAY_MAINMENU
376 /* track might have changed */
382 case ACTION_WPS_BROWSE
:
383 #ifdef HAVE_LCD_CHARCELLS
384 status_set_record(false);
385 status_set_audio(false);
388 return GO_TO_PREVIOUS_BROWSER
;
392 case ACTION_WPS_PLAY
:
393 if (global_settings
.party_mode
)
395 if ( wps_state
.paused
)
397 wps_state
.paused
= false;
398 if ( global_settings
.fade_on_stop
)
405 wps_state
.paused
= true;
406 if ( global_settings
.fade_on_stop
)
411 #if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
412 call_storage_idle_notifys(true); /* make sure resume info is saved */
417 case ACTION_WPS_VOLUP
:
420 gui_wps
[i
].data
->button_time_volume
= current_tick
;
421 global_settings
.volume
++;
426 if(update_onvol_change(&gui_wps
[i
]))
431 restoretimer
= RESTORE_WPS_NEXT_SECOND
;
435 case ACTION_WPS_VOLDOWN
:
438 gui_wps
[i
].data
->button_time_volume
= current_tick
;
439 global_settings
.volume
--;
444 if(update_onvol_change(&gui_wps
[i
]))
449 restoretimer
= RESTORE_WPS_NEXT_SECOND
;
454 OR next dir if this is straight after ACTION_WPS_SKIPNEXT */
455 case ACTION_WPS_SEEKFWD
:
456 if (global_settings
.party_mode
)
458 if (current_tick
-last_right
< HZ
)
460 if (cuesheet_is_enabled() && wps_state
.id3
->cuesheet_type
)
470 ffwd_rew(ACTION_WPS_SEEKFWD
);
471 last_right
= last_left
= 0;
474 OR prev dir if this is straight after ACTION_WPS_SKIPPREV,*/
475 case ACTION_WPS_SEEKBACK
:
476 if (global_settings
.party_mode
)
478 if (current_tick
-last_left
< HZ
)
480 if (cuesheet_is_enabled() && wps_state
.id3
->cuesheet_type
)
482 if (!wps_state
.paused
)
483 #if (CONFIG_CODEC == SWCODEC)
484 audio_pre_ff_rewind();
496 ffwd_rew(ACTION_WPS_SEEKBACK
);
497 last_left
= last_right
= 0;
501 case ACTION_WPS_SKIPPREV
:
502 if (global_settings
.party_mode
)
504 last_left
= current_tick
;
506 #ifdef AB_REPEAT_ENABLE
507 /* if we're in A/B repeat mode and the current position
508 is past the A marker, jump back to the A marker... */
509 if ( ab_repeat_mode_enabled() )
511 if ( ab_after_A_marker(wps_state
.id3
->elapsed
) )
513 ab_jump_to_A_marker();
515 #if (AB_REPEAT_ENABLE == 2)
522 /* ...otherwise, do it normally */
528 OR if skip length set, hop by predetermined amount. */
529 case ACTION_WPS_SKIPNEXT
:
530 if (global_settings
.party_mode
)
532 last_right
= current_tick
;
534 #ifdef AB_REPEAT_ENABLE
535 /* if we're in A/B repeat mode and the current position is
536 before the A marker, jump to the A marker... */
537 if ( ab_repeat_mode_enabled() )
539 if ( ab_before_A_marker(wps_state
.id3
->elapsed
) )
541 ab_jump_to_A_marker();
543 #if (AB_REPEAT_ENABLE == 2)
550 /* ...otherwise, do it normally */
554 /* next / prev directories */
555 /* and set A-B markers if in a-b mode */
556 case ACTION_WPS_ABSETB_NEXTDIR
:
557 if (global_settings
.party_mode
)
559 #if defined(AB_REPEAT_ENABLE)
560 if (ab_repeat_mode_enabled())
562 ab_set_B_marker(wps_state
.id3
->elapsed
);
563 ab_jump_to_A_marker();
572 case ACTION_WPS_ABSETA_PREVDIR
:
573 if (global_settings
.party_mode
)
575 #if defined(AB_REPEAT_ENABLE)
576 if (ab_repeat_mode_enabled())
577 ab_set_A_marker(wps_state
.id3
->elapsed
);
584 /* menu key functions */
585 case ACTION_WPS_MENU
:
591 #ifdef HAVE_QUICKSCREEN
592 case ACTION_WPS_QUICKSCREEN
:
595 if (quick_screen_quick(button
))
596 return SYS_USB_CONNECTED
;
600 #endif /* HAVE_QUICKSCREEN */
602 /* screen settings */
607 if (quick_screen_f3(BUTTON_F3
))
608 return SYS_USB_CONNECTED
;
612 #endif /* BUTTON_F3 */
615 #ifdef HAVE_PITCHSCREEN
616 case ACTION_WPS_PITCHSCREEN
:
619 if (1 == gui_syncpitchscreen_run())
620 return SYS_USB_CONNECTED
;
624 #endif /* HAVE_PITCHSCREEN */
626 #ifdef AB_REPEAT_ENABLE
627 /* reset A&B markers */
628 case ACTION_WPS_ABRESET
:
629 if (ab_repeat_mode_enabled())
635 #endif /* AB_REPEAT_ENABLE */
637 /* stop and exit wps */
638 case ACTION_WPS_STOP
:
639 if (global_settings
.party_mode
)
645 case ACTION_WPS_ID3SCREEN
:
653 case ACTION_REDRAW
: /* yes are locked, just redraw */
656 case ACTION_NONE
: /* Timeout */
658 ffwd_rew(button
); /* hopefully fix the ffw/rwd bug */
660 #ifdef HAVE_RECORDING
667 default_event_handler(SYS_POWEROFF
);
671 if(default_event_handler(button
) == SYS_USB_CONNECTED
)
681 gui_wps_update(&gui_wps
[i
]);
683 update_track
= false;
686 if (restore
&& wps_state
.id3
&&
687 ((restoretimer
== RESTORE_WPS_INSTANTLY
) ||
688 TIME_AFTER(current_tick
, restoretimer
)))
691 restoretimer
= RESTORE_WPS_INSTANTLY
;
694 screens
[i
].stop_scroll();
695 gui_wps_display(&gui_wps
[i
]);
700 #ifdef HAVE_LCD_CHARCELLS
701 status_set_record(false);
702 status_set_audio(false);
704 if (global_settings
.fade_on_stop
)
708 bookmark_autobookmark();
710 #ifdef AB_REPEAT_ENABLE
714 #ifdef HAVE_RECORDING
715 if (button
== ACTION_WPS_REC
)
716 return GO_TO_RECSCREEN
;
718 if (global_settings
.browse_current
)
719 return GO_TO_PREVIOUS_BROWSER
;
720 return GO_TO_PREVIOUS
;
723 if (button
&& !IS_SYSEVENT(button
) )
726 return GO_TO_ROOT
; /* unreachable - just to reduce compiler warnings */
729 /* needs checking if needed end*/
733 static void wps_state_init(void)
735 wps_state
.ff_rewind
= false;
736 wps_state
.paused
= false;
737 wps_state
.id3
= NULL
;
738 wps_state
.nid3
= NULL
;
743 #ifdef HAVE_LCD_BITMAP
744 static void statusbar_toggle_handler(void *data
)
748 gwps_fix_statusbars();
752 struct viewport
*vp
= &gui_wps
[i
].data
->viewports
[0].vp
;
753 bool draw
= wpsbars
& (VP_SB_ONSCREEN(i
) | VP_SB_IGNORE_SETTING(i
));
757 vp
->height
= screens
[i
].lcdheight
;
761 vp
->y
= STATUSBAR_HEIGHT
;
762 vp
->height
= screens
[i
].lcdheight
- STATUSBAR_HEIGHT
;
768 void gui_sync_wps_init(void)
773 wps_data_init(&wps_datas
[i
]);
775 wps_datas
[i
].wps_uses_albumart
= 0;
777 #ifdef HAVE_REMOTE_LCD
778 wps_datas
[i
].remote_wps
= (i
!= 0);
780 gui_wps
[i
].data
= &wps_datas
[i
];
781 gui_wps
[i
].display
= &screens
[i
];
782 /* Currently no seperate wps_state needed/possible
783 so use the only aviable ( "global" ) one */
784 gui_wps
[i
].state
= &wps_state
;
786 #ifdef HAVE_LCD_BITMAP
787 add_event(GUI_EVENT_STATUSBAR_TOGGLE
, false, statusbar_toggle_handler
);
790 unload_wps_backdrop();
792 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
793 unload_remote_wps_backdrop();
798 /* Returns true if at least one of the gui_wps screens has an album art
799 tag in its wps structure */
800 bool gui_sync_wps_uses_albumart(void)
804 struct gui_wps
*gwps
= &gui_wps
[i
];
805 if (gwps
->data
&& (gwps
->data
->wps_uses_albumart
!= WPS_ALBUMART_NONE
))