Moved most actions off of button release events.
[kugel-rb.git] / apps / wps.c
blob4a1a6f9b0249c3ecff8d5b669e300218fdc0daad
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Jerome Kuptz
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 <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
23 #include "system.h"
24 #include "file.h"
25 #include "lcd.h"
26 #include "font.h"
27 #include "backlight.h"
28 #include "button.h"
29 #include "kernel.h"
30 #include "tree.h"
31 #include "debug.h"
32 #include "sprintf.h"
33 #include "settings.h"
34 #include "wps.h"
35 #include "wps-display.h"
36 #include "mpeg.h"
37 #include "mp3_playback.h"
38 #include "usb.h"
39 #include "status.h"
40 #include "main_menu.h"
41 #include "ata.h"
42 #include "screens.h"
43 #include "playlist.h"
44 #ifdef HAVE_LCD_BITMAP
45 #include "icons.h"
46 #include "peakmeter.h"
47 #include "action.h"
48 #endif
49 #include "lang.h"
50 #include "bookmark.h"
51 #include "misc.h"
53 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
54 /* 3% of 30min file == 54s step size */
55 #define MIN_FF_REWIND_STEP 500
57 bool keys_locked = false;
58 static bool ff_rewind = false;
59 static bool paused = false;
60 static struct mp3entry* id3 = NULL;
61 static struct mp3entry* nid3 = NULL;
62 static char current_track_path[MAX_PATH+1];
64 /* button definitions */
65 #if CONFIG_KEYPAD == RECORDER_PAD
66 #define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
67 #define WPS_NEXT_PRE BUTTON_RIGHT
68 #define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
69 #define WPS_PREV_PRE BUTTON_LEFT
70 #define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
71 #define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
72 #define WPS_INCVOL BUTTON_UP
73 #define WPS_DECVOL BUTTON_DOWN
74 #define WPS_PAUSE BUTTON_PLAY
75 #define WPS_MENU (BUTTON_F1 | BUTTON_REL)
76 #define WPS_MENU_PRE BUTTON_F1
77 #define WPS_BROWSE (BUTTON_ON | BUTTON_REL)
78 #define WPS_BROWSE_PRE BUTTON_ON
79 #define WPS_EXIT BUTTON_OFF
80 #define WPS_KEYLOCK (BUTTON_F1 | BUTTON_DOWN)
81 #define WPS_ID3 (BUTTON_F1 | BUTTON_ON)
83 #define WPS_RC_NEXT BUTTON_RC_RIGHT
84 #define WPS_RC_PREV BUTTON_RC_LEFT
85 #define WPS_RC_PAUSE BUTTON_RC_PLAY
86 #define WPS_RC_INCVOL BUTTON_RC_VOL_UP
87 #define WPS_RC_DECVOL BUTTON_RC_VOL_DOWN
88 #define WPS_RC_EXIT BUTTON_RC_STOP
90 #elif CONFIG_KEYPAD == PLAYER_PAD
91 #define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
92 #define WPS_NEXT_PRE BUTTON_RIGHT
93 #define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
94 #define WPS_PREV_PRE BUTTON_LEFT
95 #define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
96 #define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
97 #define WPS_INCVOL (BUTTON_MENU | BUTTON_RIGHT)
98 #define WPS_DECVOL (BUTTON_MENU | BUTTON_LEFT)
99 #define WPS_PAUSE BUTTON_PLAY
100 #define WPS_MENU (BUTTON_MENU | BUTTON_REL)
101 #define WPS_MENU_PRE BUTTON_MENU
102 #define WPS_BROWSE (BUTTON_ON | BUTTON_REL)
103 #define WPS_BROWSE_PRE BUTTON_ON
104 #define WPS_EXIT BUTTON_STOP
105 #define WPS_KEYLOCK (BUTTON_MENU | BUTTON_STOP)
106 #define WPS_ID3 (BUTTON_MENU | BUTTON_ON)
108 #define WPS_RC_NEXT BUTTON_RC_RIGHT
109 #define WPS_RC_PREV BUTTON_RC_LEFT
110 #define WPS_RC_PAUSE BUTTON_RC_PLAY
111 #define WPS_RC_INCVOL BUTTON_RC_VOL_UP
112 #define WPS_RC_DECVOL BUTTON_RC_VOL_DOWN
113 #define WPS_RC_EXIT BUTTON_RC_STOP
115 #elif CONFIG_KEYPAD == ONDIO_PAD
116 #define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL)
117 #define WPS_NEXT_PRE BUTTON_RIGHT
118 #define WPS_PREV (BUTTON_LEFT | BUTTON_REL)
119 #define WPS_PREV_PRE BUTTON_LEFT
120 #define WPS_FFWD (BUTTON_RIGHT | BUTTON_REPEAT)
121 #define WPS_REW (BUTTON_LEFT | BUTTON_REPEAT)
122 #define WPS_INCVOL BUTTON_UP
123 #define WPS_DECVOL BUTTON_DOWN
124 #define WPS_PAUSE BUTTON_OFF
125 #define WPS_MENU (BUTTON_MENU | BUTTON_REPEAT)
126 #define WPS_BROWSE (BUTTON_MENU | BUTTON_REL)
127 #define WPS_BROWSE_PRE BUTTON_MENU
128 #define WPS_KEYLOCK (BUTTON_MENU | BUTTON_DOWN)
130 #endif
132 /* set volume
133 return true if screen restore is needed
134 return false otherwise
136 static bool setvol(void)
138 if (global_settings.volume < mpeg_sound_min(SOUND_VOLUME))
139 global_settings.volume = mpeg_sound_min(SOUND_VOLUME);
140 if (global_settings.volume > mpeg_sound_max(SOUND_VOLUME))
141 global_settings.volume = mpeg_sound_max(SOUND_VOLUME);
142 mpeg_sound_set(SOUND_VOLUME, global_settings.volume);
143 status_draw(false);
144 wps_refresh(id3, nid3, 0, WPS_REFRESH_NON_STATIC);
145 settings_save();
146 #ifdef HAVE_LCD_CHARCELLS
147 splash(0, false, "Vol: %d %% ",
148 mpeg_val2phys(SOUND_VOLUME, global_settings.volume));
149 return true;
150 #endif
151 return false;
154 static void display_keylock_text(bool locked)
156 char* s;
157 lcd_stop_scroll();
158 #ifdef HAVE_LCD_CHARCELLS
159 if(locked)
160 s = str(LANG_KEYLOCK_ON_PLAYER);
161 else
162 s = str(LANG_KEYLOCK_OFF_PLAYER);
163 #else
164 if(locked)
165 s = str(LANG_KEYLOCK_ON_RECORDER);
166 else
167 s = str(LANG_KEYLOCK_OFF_RECORDER);
168 #endif
169 splash(HZ, true, s);
172 static bool ffwd_rew(int button)
174 static const int ff_rew_steps[] = {
175 1000, 2000, 3000, 4000,
176 5000, 6000, 8000, 10000,
177 15000, 20000, 25000, 30000,
178 45000, 60000
181 unsigned int step = 0; /* current ff/rewind step */
182 unsigned int max_step = 0; /* maximum ff/rewind step */
183 int ff_rewind_count = 0; /* current ff/rewind count (in ticks) */
184 int direction = -1; /* forward=1 or backward=-1 */
185 long accel_tick = 0; /* next time at which to bump the step size */
186 bool exit = false;
187 bool usb = false;
189 while (!exit) {
190 switch ( button ) {
191 case WPS_FFWD:
192 direction = 1;
193 case WPS_REW:
194 if (ff_rewind)
196 if (direction == 1)
198 /* fast forwarding, calc max step relative to end */
199 max_step =
200 (id3->length - (id3->elapsed + ff_rewind_count)) *
201 FF_REWIND_MAX_PERCENT / 100;
203 else
205 /* rewinding, calc max step relative to start */
206 max_step = (id3->elapsed + ff_rewind_count) *
207 FF_REWIND_MAX_PERCENT / 100;
210 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
212 if (step > max_step)
213 step = max_step;
215 ff_rewind_count += step * direction;
217 if (global_settings.ff_rewind_accel != 0 &&
218 current_tick >= accel_tick)
220 step *= 2;
221 accel_tick = current_tick +
222 global_settings.ff_rewind_accel*HZ;
225 else
227 if ( (mpeg_status() & MPEG_STATUS_PLAY) &&
228 id3 && id3->length )
230 if (!paused)
231 mpeg_pause();
232 #if CONFIG_KEYPAD == PLAYER_PAD
233 lcd_stop_scroll();
234 #endif
235 if (direction > 0)
236 status_set_ffmode(STATUS_FASTFORWARD);
237 else
238 status_set_ffmode(STATUS_FASTBACKWARD);
240 ff_rewind = true;
242 step = ff_rew_steps[global_settings.ff_rewind_min_step];
244 accel_tick = current_tick +
245 global_settings.ff_rewind_accel*HZ;
247 else
248 break;
251 if (direction > 0) {
252 if ((id3->elapsed + ff_rewind_count) > id3->length)
253 ff_rewind_count = id3->length - id3->elapsed;
255 else {
256 if ((int)(id3->elapsed + ff_rewind_count) < 0)
257 ff_rewind_count = -id3->elapsed;
260 if(wps_time_countup == false)
261 wps_refresh(id3, nid3, -ff_rewind_count,
262 WPS_REFRESH_PLAYER_PROGRESS |
263 WPS_REFRESH_DYNAMIC);
264 else
265 wps_refresh(id3, nid3, ff_rewind_count,
266 WPS_REFRESH_PLAYER_PROGRESS |
267 WPS_REFRESH_DYNAMIC);
269 break;
271 case WPS_PREV:
272 case WPS_NEXT:
273 mpeg_ff_rewind(id3->elapsed+ff_rewind_count);
274 ff_rewind_count = 0;
275 ff_rewind = false;
276 status_set_ffmode(0);
277 if (!paused)
278 mpeg_resume();
279 #ifdef HAVE_LCD_CHARCELLS
280 wps_display(id3, nid3);
281 #endif
282 exit = true;
283 break;
285 default:
286 if(default_event_handler(button) == SYS_USB_CONNECTED) {
287 status_set_ffmode(0);
288 usb = true;
289 exit = true;
291 break;
293 if (!exit)
294 button = button_get(true);
297 /* let mpeg thread update id3->elapsed before calling wps_refresh */
298 yield();
299 wps_refresh(id3, nid3, 0, WPS_REFRESH_ALL);
300 return usb;
303 static bool update(void)
305 bool track_changed = mpeg_has_changed_track();
306 bool retcode = false;
308 nid3 = mpeg_next_track();
309 if (track_changed)
311 lcd_stop_scroll();
312 id3 = mpeg_current_track();
313 if (wps_display(id3, nid3))
314 retcode = true;
315 else
316 wps_refresh(id3, nid3, 0, WPS_REFRESH_ALL);
318 if (id3)
319 memcpy(current_track_path, id3->path, sizeof(current_track_path));
322 if (id3)
323 wps_refresh(id3, nid3, 0, WPS_REFRESH_NON_STATIC);
325 status_draw(false);
327 /* save resume data */
328 if ( id3 &&
329 global_settings.resume &&
330 global_settings.resume_offset != id3->offset ) {
331 DEBUGF("R%X,%X (%X)\n", global_settings.resume_offset,
332 id3->offset,id3);
334 if (!playlist_get_resume_info(&global_settings.resume_index))
336 global_settings.resume_offset = id3->offset;
337 settings_save();
340 else if ( !id3 && track_changed ) {
341 global_settings.resume_index = -1;
342 global_settings.resume_offset = -1;
343 settings_save();
346 return retcode;
349 static void fade(bool fade_in)
351 if (fade_in) {
352 /* fade in */
353 int current_volume = 20;
355 /* zero out the sound */
356 mpeg_sound_set(SOUND_VOLUME, current_volume);
358 sleep(HZ/10); /* let mpeg thread run */
359 mpeg_resume();
361 while (current_volume < global_settings.volume) {
362 current_volume += 2;
363 sleep(1);
364 mpeg_sound_set(SOUND_VOLUME, current_volume);
366 mpeg_sound_set(SOUND_VOLUME, global_settings.volume);
368 else {
369 /* fade out */
370 int current_volume = global_settings.volume;
372 while (current_volume > 20) {
373 current_volume -= 2;
374 sleep(1);
375 mpeg_sound_set(SOUND_VOLUME, current_volume);
377 mpeg_pause();
378 sleep(HZ/5); /* let mpeg thread run */
380 /* reset volume to what it was before the fade */
381 mpeg_sound_set(SOUND_VOLUME, global_settings.volume);
386 static void waitfor_nokey(void)
388 /* wait until all keys are released */
389 while (button_get(false) != BUTTON_NONE)
390 yield();
393 /* demonstrates showing different formats from playtune */
394 int wps_show(void)
396 int button = 0, lastbutton = 0;
397 bool ignore_keyup = true;
398 bool restore = false;
399 long restoretimer = 0; /* timer to delay screen redraw temporarily */
400 bool exit = false;
401 bool update_track = false;
403 id3 = nid3 = NULL;
404 current_track_path[0] = '\0';
406 #ifdef HAVE_LCD_CHARCELLS
407 status_set_audio(true);
408 status_set_param(false);
409 #else
410 if(global_settings.statusbar)
411 lcd_setmargins(0, STATUSBAR_HEIGHT);
412 else
413 lcd_setmargins(0, 0);
414 #endif
416 ff_rewind = false;
418 if(mpeg_status() & MPEG_STATUS_PLAY)
420 id3 = mpeg_current_track();
421 nid3 = mpeg_next_track();
422 if (id3) {
423 if (wps_display(id3, nid3))
424 return 0;
425 wps_refresh(id3, nid3, 0, WPS_REFRESH_ALL);
427 memcpy(current_track_path, id3->path, sizeof(current_track_path));
430 restore = true;
433 while ( 1 )
435 bool mpeg_paused = (mpeg_status() & MPEG_STATUS_PAUSE)?true:false;
437 /* did someone else (i.e power thread) change mpeg pause mode? */
438 if (paused != mpeg_paused) {
439 paused = mpeg_paused;
441 /* if another thread paused mpeg, we are probably in car mode,
442 about to shut down. lets save the settings. */
443 if (paused && global_settings.resume) {
444 settings_save();
445 #ifndef HAVE_RTC
446 ata_flush();
447 #endif
451 #ifdef HAVE_LCD_BITMAP
452 /* when the peak meter is enabled we want to have a
453 few extra updates to make it look smooth. On the
454 other hand we don't want to waste energy if it
455 isn't displayed */
456 if (peak_meter_enabled) {
457 int i;
459 /* In high performance mode we read out the mas as
460 often as we can. There is no sleep for cpu */
461 if (global_settings.peak_meter_performance) {
462 long next_refresh = current_tick;
463 long next_big_refresh = current_tick + HZ / 5;
464 button = BUTTON_NONE;
465 while (!TIME_AFTER(current_tick, next_big_refresh)) {
466 button = button_get(false);
467 if (button != BUTTON_NONE) {
468 break;
470 peak_meter_peek();
471 sleep(1);
473 if (TIME_AFTER(current_tick, next_refresh)) {
474 wps_refresh(id3, nid3, 0, WPS_REFRESH_PEAK_METER);
475 next_refresh = current_tick + HZ / peak_meter_fps;
480 /* In energy saver mode the cpu may sleep a
481 little bit while waiting for buttons */
482 else {
483 for (i = 0; i < 4; i++) {
484 button = button_get_w_tmo(HZ / peak_meter_fps);
485 if (button != 0) {
486 break;
488 wps_refresh(id3, nid3, 0, WPS_REFRESH_PEAK_METER);
493 /* The peak meter is disabled
494 -> no additional screen updates needed */
495 else {
496 button = button_get_w_tmo(HZ/5);
498 #else
499 button = button_get_w_tmo(HZ/5);
500 #endif
502 /* discard first event if it's a button release */
503 if (button && ignore_keyup)
505 ignore_keyup = false;
506 /* Negative events are system events */
507 if (button >= 0 && button & BUTTON_REL )
508 continue;
511 #ifdef WPS_KEYLOCK
512 /* ignore non-remote buttons when keys are locked */
513 if (keys_locked &&
514 ! ((button < 0) ||
515 (button == BUTTON_NONE) ||
516 ((button & WPS_KEYLOCK) == WPS_KEYLOCK) ||
517 (button & BUTTON_REMOTE)
520 if (!(button & BUTTON_REL))
521 display_keylock_text(true);
522 restore = true;
523 button = BUTTON_NONE;
525 #endif
527 /* Exit if mpeg has stopped playing. This can happen if using the
528 sleep timer with the charger plugged or if starting a recording
529 from F1 */
530 if (!mpeg_status())
531 exit = true;
533 switch(button)
535 case WPS_BROWSE:
536 #ifdef WPS_BROWSE_PRE
537 if (lastbutton != WPS_BROWSE_PRE)
538 break;
539 #endif
540 #ifdef HAVE_LCD_CHARCELLS
541 status_set_record(false);
542 status_set_audio(false);
543 #endif
544 lcd_stop_scroll();
546 /* set dir browser to current playing song */
547 if (global_settings.browse_current &&
548 current_track_path[0] != '\0')
549 set_current_file(current_track_path);
551 return 0;
552 break;
554 /* play/pause */
555 case WPS_PAUSE:
556 #ifdef WPS_RC_PAUSE
557 case WPS_RC_PAUSE:
558 #endif
559 if ( paused )
561 paused = false;
562 if ( global_settings.fade_on_stop )
563 fade(1);
564 else
565 mpeg_resume();
567 else
569 paused = true;
570 if ( global_settings.fade_on_stop )
571 fade(0);
572 else
573 mpeg_pause();
574 if (global_settings.resume) {
575 settings_save();
576 #ifndef HAVE_RTC
577 ata_flush();
578 #endif
581 break;
583 /* volume up */
584 case WPS_INCVOL:
585 case WPS_INCVOL | BUTTON_REPEAT:
586 #ifdef WPS_RC_INCVOL
587 case WPS_RC_INCVOL:
588 #endif
589 global_settings.volume++;
590 if (setvol()) {
591 restore = true;
592 restoretimer = current_tick + HZ;
594 break;
596 /* volume down */
597 case WPS_DECVOL:
598 case WPS_DECVOL | BUTTON_REPEAT:
599 #ifdef WPS_RC_DECVOL
600 case WPS_RC_DECVOL:
601 #endif
602 global_settings.volume--;
603 if (setvol()) {
604 restore = true;
605 restoretimer = current_tick + HZ;
607 break;
609 /* fast forward / rewind */
610 case WPS_FFWD:
611 case WPS_REW:
612 #ifdef WPS_RC_FFWD
613 case WPS_RC_FFWD:
614 case WPS_RC_RWD:
615 #endif
616 ffwd_rew(button);
617 break;
619 /* prev / restart */
620 #ifdef WPS_RC_PREV
621 case WPS_RC_PREV:
622 #endif
623 case WPS_PREV:
624 #ifdef WPS_PREV_PRE
625 if (lastbutton != WPS_PREV_PRE)
626 break;
627 #endif
628 if (!id3 || (id3->elapsed < 3*1000)) {
629 mpeg_prev();
631 else {
632 if (!paused)
633 mpeg_pause();
635 mpeg_ff_rewind(0);
637 if (!paused)
638 mpeg_resume();
640 break;
642 /* next */
643 #ifdef WPS_RC_NEXT
644 case WPS_RC_NEXT:
645 #endif
646 case WPS_NEXT:
647 #ifdef WPS_NEXT_PRE
648 if (lastbutton != WPS_NEXT_PRE)
649 break;
650 #endif
651 mpeg_next();
652 break;
654 /* menu key functions */
655 case WPS_MENU:
656 #ifdef WPS_MENU_PRE
657 if (lastbutton != WPS_MENU_PRE)
658 break;
659 #endif
660 lcd_stop_scroll();
662 if (main_menu())
663 return true;
664 #ifdef HAVE_LCD_BITMAP
665 if (global_settings.statusbar)
666 lcd_setmargins(0, STATUSBAR_HEIGHT);
667 else
668 lcd_setmargins(0, 0);
669 #endif
670 restore = true;
671 break;
673 /* key lock */
674 case WPS_KEYLOCK:
675 case WPS_KEYLOCK | BUTTON_REPEAT:
676 keys_locked = !keys_locked;
677 display_keylock_text(keys_locked);
678 restore = true;
679 waitfor_nokey();
680 break;
682 #if CONFIG_KEYPAD == RECORDER_PAD
683 /* play settings */
684 case BUTTON_F2:
685 if (quick_screen(CONTEXT_WPS, BUTTON_F2))
686 return SYS_USB_CONNECTED;
687 restore = true;
688 break;
690 /* screen settings */
691 case BUTTON_F3:
692 if (quick_screen(CONTEXT_WPS, BUTTON_F3))
693 return SYS_USB_CONNECTED;
694 restore = true;
695 break;
697 /* pitch screen */
698 case BUTTON_ON | BUTTON_REPEAT:
699 if (2 == pitch_screen())
700 return SYS_USB_CONNECTED;
701 restore = true;
702 break;
703 #endif
705 /* stop and exit wps */
706 #ifdef WPS_EXIT
707 case WPS_EXIT:
708 #ifdef WPS_RC_EXIT
709 case WPS_RC_EXIT:
710 #endif
711 exit = true;
712 break;
713 #endif
715 #ifdef WPS_ID3
716 case WPS_ID3:
717 browse_id3();
718 restore = true;
719 break;
720 #endif
722 case BUTTON_NONE: /* Timeout */
723 update_track = true;
724 break;
726 default:
727 if(default_event_handler(button) == SYS_USB_CONNECTED)
728 return SYS_USB_CONNECTED;
729 break;
732 if (update_track)
734 if (update())
736 /* set dir browser to current playing song */
737 if (global_settings.browse_current &&
738 current_track_path[0] != '\0')
739 set_current_file(current_track_path);
741 return 0;
743 update_track = false;
746 if (exit) {
747 #ifdef HAVE_LCD_CHARCELLS
748 status_set_record(false);
749 status_set_audio(false);
750 #endif
751 if (global_settings.fade_on_stop)
752 fade(0);
754 lcd_stop_scroll();
755 bookmark_autobookmark();
756 mpeg_stop();
758 /* Keys can be locked when exiting, so either unlock here
759 or implement key locking in tree.c too */
760 keys_locked=false;
762 /* set dir browser to current playing song */
763 if (global_settings.browse_current &&
764 current_track_path[0] != '\0')
765 set_current_file(current_track_path);
767 return 0;
770 if ( button )
771 ata_spin();
773 if (restore &&
774 ((restoretimer == 0) ||
775 (restoretimer < current_tick)))
777 restore = false;
778 restoretimer = 0;
779 if (wps_display(id3, nid3))
781 /* set dir browser to current playing song */
782 if (global_settings.browse_current &&
783 current_track_path[0] != '\0')
784 set_current_file(current_track_path);
786 return 0;
789 if (id3)
790 wps_refresh(id3, nid3, 0, WPS_REFRESH_NON_STATIC);
792 if (button != BUTTON_NONE)
793 lastbutton = button;
795 return 0; /* unreachable - just to reduce compiler warnings */