/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2002 Jerome Kuptz * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include #include #include #include "system.h" #include "file.h" #include "lcd.h" #include "font.h" #include "backlight.h" #include "button.h" #include "kernel.h" #include "tree.h" #include "debug.h" #include "sprintf.h" #include "settings.h" #include "wps.h" #include "wps-display.h" #include "audio.h" #include "mp3_playback.h" #include "usb.h" #include "status.h" #include "main_menu.h" #include "ata.h" #include "screens.h" #include "playlist.h" #ifdef HAVE_LCD_BITMAP #include "icons.h" #include "peakmeter.h" #endif #include "action.h" #include "lang.h" #include "bookmark.h" #include "misc.h" #include "sound.h" #include "onplay.h" #include "abrepeat.h" #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */ /* 3% of 30min file == 54s step size */ #define MIN_FF_REWIND_STEP 500 bool keys_locked = false; static bool ff_rewind = false; static bool paused = false; static struct mp3entry* id3 = NULL; static struct mp3entry* nid3 = NULL; static char current_track_path[MAX_PATH+1]; /* set volume return true if screen restore is needed return false otherwise */ static bool setvol(void) { if (global_settings.volume < sound_min(SOUND_VOLUME)) global_settings.volume = sound_min(SOUND_VOLUME); if (global_settings.volume > sound_max(SOUND_VOLUME)) global_settings.volume = sound_max(SOUND_VOLUME); sound_set(SOUND_VOLUME, global_settings.volume); status_draw(false); wps_refresh(id3, nid3, 0, WPS_REFRESH_NON_STATIC); settings_save(); #ifdef HAVE_LCD_CHARCELLS splash(0, false, "Vol: %d %% ", sound_val2phys(SOUND_VOLUME, global_settings.volume)); return true; #endif return false; } static bool ffwd_rew(int button) { static const int ff_rew_steps[] = { 1000, 2000, 3000, 4000, 5000, 6000, 8000, 10000, 15000, 20000, 25000, 30000, 45000, 60000 }; unsigned int step = 0; /* current ff/rewind step */ unsigned int max_step = 0; /* maximum ff/rewind step */ int ff_rewind_count = 0; /* current ff/rewind count (in ticks) */ int direction = -1; /* forward=1 or backward=-1 */ long accel_tick = 0; /* next time at which to bump the step size */ bool exit = false; bool usb = false; while (!exit) { switch ( button ) { case WPS_FFWD: #ifdef WPS_RC_FFWD case WPS_RC_FFWD: #endif direction = 1; case WPS_REW: #ifdef WPS_RC_REW case WPS_RC_REW: #endif if (ff_rewind) { if (direction == 1) { /* fast forwarding, calc max step relative to end */ max_step = (id3->length - (id3->elapsed + ff_rewind_count)) * FF_REWIND_MAX_PERCENT / 100; } else { /* rewinding, calc max step relative to start */ max_step = (id3->elapsed + ff_rewind_count) * FF_REWIND_MAX_PERCENT / 100; } max_step = MAX(max_step, MIN_FF_REWIND_STEP); if (step > max_step) step = max_step; ff_rewind_count += step * direction; if (global_settings.ff_rewind_accel != 0 && current_tick >= accel_tick) { step *= 2; accel_tick = current_tick + global_settings.ff_rewind_accel*HZ; } } else { if ( (audio_status() & AUDIO_STATUS_PLAY) && id3 && id3->length ) { if (!paused) audio_pause(); #if CONFIG_KEYPAD == PLAYER_PAD lcd_stop_scroll(); #endif if (direction > 0) status_set_ffmode(STATUS_FASTFORWARD); else status_set_ffmode(STATUS_FASTBACKWARD); ff_rewind = true; step = ff_rew_steps[global_settings.ff_rewind_min_step]; accel_tick = current_tick + global_settings.ff_rewind_accel*HZ; } else break; } if (direction > 0) { if ((id3->elapsed + ff_rewind_count) > id3->length) ff_rewind_count = id3->length - id3->elapsed; } else { if ((int)(id3->elapsed + ff_rewind_count) < 0) ff_rewind_count = -id3->elapsed; } if(wps_time_countup == false) wps_refresh(id3, nid3, -ff_rewind_count, WPS_REFRESH_PLAYER_PROGRESS | WPS_REFRESH_DYNAMIC); else wps_refresh(id3, nid3, ff_rewind_count, WPS_REFRESH_PLAYER_PROGRESS | WPS_REFRESH_DYNAMIC); break; case WPS_PREV: case WPS_NEXT: #ifdef WPS_RC_PREV case WPS_RC_PREV: case WPS_RC_NEXT: #endif audio_ff_rewind(id3->elapsed+ff_rewind_count); ff_rewind_count = 0; ff_rewind = false; status_set_ffmode(0); if (!paused) audio_resume(); #ifdef HAVE_LCD_CHARCELLS wps_display(id3, nid3); #endif exit = true; break; default: if(default_event_handler(button) == SYS_USB_CONNECTED) { status_set_ffmode(0); usb = true; exit = true; } break; } if (!exit) button = button_get(true); } /* let audio thread update id3->elapsed before calling wps_refresh */ yield(); wps_refresh(id3, nid3, 0, WPS_REFRESH_ALL); return usb; } static bool update(void) { bool track_changed = audio_has_changed_track(); bool retcode = false; nid3 = audio_next_track(); if (track_changed) { lcd_stop_scroll(); id3 = audio_current_track(); if (wps_display(id3, nid3)) retcode = true; else wps_refresh(id3, nid3, 0, WPS_REFRESH_ALL); if (id3) memcpy(current_track_path, id3->path, sizeof(current_track_path)); } if (id3) wps_refresh(id3, nid3, 0, WPS_REFRESH_NON_STATIC); status_draw(false); return retcode; } static void fade(bool fade_in) { unsigned fp_global_vol = global_settings.volume << 8; unsigned fp_step = fp_global_vol / 30; if (fade_in) { /* fade in */ unsigned fp_volume = 0; /* zero out the sound */ sound_set(SOUND_VOLUME, 0); sleep(HZ/10); /* let audio thread run */ audio_resume(); while (fp_volume < fp_global_vol) { fp_volume += fp_step; sleep(1); sound_set(SOUND_VOLUME, fp_volume >> 8); } sound_set(SOUND_VOLUME, global_settings.volume); } else { /* fade out */ unsigned fp_volume = fp_global_vol; while (fp_volume > fp_step) { fp_volume -= fp_step; sleep(1); sound_set(SOUND_VOLUME, fp_volume >> 8); } audio_pause(); #ifndef SIMULATOR /* let audio thread run and wait for the mas to run out of data */ while (!mp3_pause_done()) #endif sleep(HZ/10); /* reset volume to what it was before the fade */ sound_set(SOUND_VOLUME, global_settings.volume); } } #ifdef WPS_KEYLOCK static void display_keylock_text(bool locked) { char* s; lcd_stop_scroll(); #ifdef HAVE_LCD_CHARCELLS if(locked) s = str(LANG_KEYLOCK_ON_PLAYER); else s = str(LANG_KEYLOCK_OFF_PLAYER); #else if(locked) s = str(LANG_KEYLOCK_ON_RECORDER); else s = str(LANG_KEYLOCK_OFF_RECORDER); #endif splash(HZ, true, s); } static void waitfor_nokey(void) { /* wait until all keys are released */ while (button_get(false) != BUTTON_NONE) yield(); } #endif /* demonstrates showing different formats from playtune */ long wps_show(void) { long button = 0, lastbutton = 0; bool ignore_keyup = true; bool restore = false; long restoretimer = 0; /* timer to delay screen redraw temporarily */ bool exit = false; bool update_track = false; id3 = nid3 = NULL; current_track_path[0] = '\0'; #ifdef HAVE_LCD_CHARCELLS status_set_audio(true); status_set_param(false); #else if(global_settings.statusbar) lcd_setmargins(0, STATUSBAR_HEIGHT); else lcd_setmargins(0, 0); #endif #ifdef AB_REPEAT_ENABLE ab_repeat_init(); ab_reset_markers(); #endif ff_rewind = false; if(audio_status() & AUDIO_STATUS_PLAY) { id3 = audio_current_track(); nid3 = audio_next_track(); if (id3) { if (wps_display(id3, nid3)) return 0; wps_refresh(id3, nid3, 0, WPS_REFRESH_ALL); memcpy(current_track_path, id3->path, sizeof(current_track_path)); } restore = true; } while ( 1 ) { bool audio_paused = (audio_status() & AUDIO_STATUS_PAUSE)?true:false; /* did someone else (i.e power thread) change audio pause mode? */ if (paused != audio_paused) { paused = audio_paused; /* if another thread paused audio, we are probably in car mode, about to shut down. lets save the settings. */ if (paused) { settings_save(); #if !defined(HAVE_RTC) && !defined(HAVE_SW_POWEROFF) ata_flush(); #endif } } #ifdef HAVE_LCD_BITMAP /* when the peak meter is enabled we want to have a few extra updates to make it look smooth. On the other hand we don't want to waste energy if it isn't displayed */ if (peak_meter_enabled) { long next_refresh = current_tick; long next_big_refresh = current_tick + HZ / 5; button = BUTTON_NONE; while (TIME_BEFORE(current_tick, next_big_refresh)) { button = button_get(false); if (button != BUTTON_NONE) { break; } peak_meter_peek(); sleep(0); /* Sleep until end of current tick. */ if (TIME_AFTER(current_tick, next_refresh)) { wps_refresh(id3, nid3, 0, WPS_REFRESH_PEAK_METER); next_refresh += HZ / PEAK_METER_FPS; } } } /* The peak meter is disabled -> no additional screen updates needed */ else { button = button_get_w_tmo(HZ/5); } #else button = button_get_w_tmo(HZ/5); #endif /* discard first event if it's a button release */ if (button && ignore_keyup) { ignore_keyup = false; /* Negative events are system events */ if (button >= 0 && button & BUTTON_REL ) continue; } #ifdef WPS_KEYLOCK /* ignore non-remote buttons when keys are locked */ if (keys_locked && ! ((button < 0) || (button == BUTTON_NONE) || ((button & WPS_KEYLOCK) == WPS_KEYLOCK) || (button & BUTTON_REMOTE) )) { if (!(button & BUTTON_REL)) display_keylock_text(true); restore = true; button = BUTTON_NONE; } #endif /* Exit if audio has stopped playing. This can happen if using the sleep timer with the charger plugged or if starting a recording from F1 */ if (!audio_status()) exit = true; switch(button) { #ifdef WPS_CONTEXT case WPS_CONTEXT: onplay(id3->path, TREE_ATTR_MPA, CONTEXT_WPS); restore = true; break; #endif #ifdef WPS_RC_BROWSE case WPS_RC_BROWSE: #endif case WPS_BROWSE: #ifdef WPS_BROWSE_PRE if ((lastbutton != WPS_BROWSE_PRE) #ifdef WPS_RC_BROWSE_PRE && (lastbutton != WPS_RC_BROWSE_PRE) #endif ) break; #endif #ifdef HAVE_LCD_CHARCELLS status_set_record(false); status_set_audio(false); #endif lcd_stop_scroll(); /* set dir browser to current playing song */ if (global_settings.browse_current && current_track_path[0] != '\0') set_current_file(current_track_path); return 0; break; /* play/pause */ case WPS_PAUSE: #ifdef WPS_PAUSE_PRE if (lastbutton != WPS_PAUSE_PRE) break; #endif #ifdef WPS_RC_PAUSE case WPS_RC_PAUSE: #ifdef WPS_RC_PAUSE_PRE if ((button == WPS_RC_PAUSE) && (lastbutton != WPS_RC_PAUSE_PRE)) break; #endif #endif if ( paused ) { paused = false; if ( global_settings.fade_on_stop ) fade(1); else audio_resume(); } else { paused = true; if ( global_settings.fade_on_stop ) fade(0); else audio_pause(); settings_save(); #if !defined(HAVE_RTC) && !defined(HAVE_SW_POWEROFF) ata_flush(); /* make sure resume info is saved */ #endif } break; /* volume up */ case WPS_INCVOL: case WPS_INCVOL | BUTTON_REPEAT: #ifdef WPS_RC_INCVOL case WPS_RC_INCVOL: case WPS_RC_INCVOL | BUTTON_REPEAT: #endif global_settings.volume++; if (setvol()) { restore = true; restoretimer = current_tick + HZ; } break; /* volume down */ case WPS_DECVOL: case WPS_DECVOL | BUTTON_REPEAT: #ifdef WPS_RC_DECVOL case WPS_RC_DECVOL: case WPS_RC_DECVOL | BUTTON_REPEAT: #endif global_settings.volume--; if (setvol()) { restore = true; restoretimer = current_tick + HZ; } break; /* fast forward / rewind */ case WPS_FFWD: case WPS_REW: #ifdef WPS_RC_FFWD case WPS_RC_FFWD: case WPS_RC_REW: #endif ffwd_rew(button); break; /* prev / restart */ case WPS_PREV: #ifdef WPS_PREV_PRE if (lastbutton != WPS_PREV_PRE) break; #endif #ifdef WPS_RC_PREV case WPS_RC_PREV: #ifdef WPS_RC_PREV_PRE if ((button == WPS_RC_PREV) && (lastbutton != WPS_RC_PREV_PRE)) break; #endif #endif #ifdef AB_REPEAT_ENABLE /* if we're in A/B repeat mode and the current position is past the A marker, jump back to the A marker... */ if ( ab_repeat_mode_enabled() && ab_after_A_marker(id3->elapsed) ) { ab_jump_to_A_marker(); update_track = true; break; } /* ...otherwise, do it normally */ #endif if (!id3 || (id3->elapsed < 3*1000)) { audio_prev(); } else { if (!paused) audio_pause(); audio_ff_rewind(0); if (!paused) audio_resume(); } break; /* next */ case WPS_NEXT: #ifdef WPS_NEXT_PRE if (lastbutton != WPS_NEXT_PRE) break; #endif #ifdef WPS_RC_NEXT case WPS_RC_NEXT: #ifdef WPS_RC_NEXT_PRE if ((button == WPS_RC_NEXT) && (lastbutton != WPS_RC_NEXT_PRE)) break; #endif #endif #ifdef AB_REPEAT_ENABLE /* if we're in A/B repeat mode and the current position is before the A marker, jump to the A marker... */ if ( ab_repeat_mode_enabled() && ab_before_A_marker(id3->elapsed) ) { ab_jump_to_A_marker(); update_track = true; break; } /* ...otherwise, do it normally */ #endif audio_next(); break; #ifdef WPS_MENU /* menu key functions */ case WPS_MENU: #ifdef WPS_MENU_PRE if (lastbutton != WPS_MENU_PRE) break; #endif #ifdef WPS_RC_MENU case WPS_RC_MENU: #ifdef WPS_RC_MENU_PRE if ((button == WPS_RC_MENU) && (lastbutton != WPS_RC_MENU_PRE)) break; #endif #endif lcd_stop_scroll(); if (main_menu()) return true; #ifdef HAVE_LCD_BITMAP if (global_settings.statusbar) lcd_setmargins(0, STATUSBAR_HEIGHT); else lcd_setmargins(0, 0); #endif restore = true; break; #endif /* WPS_MENU */ #ifdef WPS_KEYLOCK /* key lock */ case WPS_KEYLOCK: case WPS_KEYLOCK | BUTTON_REPEAT: keys_locked = !keys_locked; display_keylock_text(keys_locked); restore = true; waitfor_nokey(); break; #endif #if (CONFIG_KEYPAD == RECORDER_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD) /* play settings */ case WPS_QUICK: if (quick_screen(CONTEXT_WPS, WPS_QUICK)) return SYS_USB_CONNECTED; restore = true; lastbutton = 0; break; /* screen settings */ #ifdef BUTTON_F3 case BUTTON_F3: if (quick_screen(CONTEXT_WPS, BUTTON_F3)) return SYS_USB_CONNECTED; restore = true; break; #endif /* pitch screen */ #if CONFIG_KEYPAD == RECORDER_PAD case BUTTON_ON | BUTTON_UP: case BUTTON_ON | BUTTON_DOWN: if (2 == pitch_screen()) return SYS_USB_CONNECTED; restore = true; break; #endif #endif #ifdef AB_REPEAT_ENABLE #ifdef WPS_AB_SET_A_MARKER /* set A marker for A-B repeat */ case WPS_AB_SET_A_MARKER: if (ab_repeat_mode_enabled()) ab_set_A_marker(id3->elapsed); break; #endif #ifdef WPS_AB_SET_B_MARKER /* set B marker for A-B repeat and jump to A */ case WPS_AB_SET_B_MARKER: if (ab_repeat_mode_enabled()) { ab_set_B_marker(id3->elapsed); ab_jump_to_A_marker(); update_track = true; } break; #endif #ifdef WPS_AB_RESET_AB_MARKERS /* reset A&B markers */ case WPS_AB_RESET_AB_MARKERS: if (ab_repeat_mode_enabled()) { ab_reset_markers(); update_track = true; } break; #endif #endif /* AB_REPEAT_ENABLE */ /* stop and exit wps */ #ifdef WPS_EXIT case WPS_EXIT: #ifdef WPS_RC_EXIT case WPS_RC_EXIT: #endif exit = true; break; #endif #ifdef WPS_ID3 case WPS_ID3: browse_id3(); restore = true; break; #endif case BUTTON_NONE: /* Timeout */ update_track = true; break; default: if(default_event_handler(button) == SYS_USB_CONNECTED) return SYS_USB_CONNECTED; update_track = true; break; } if (update_track) { if (update()) { /* set dir browser to current playing song */ if (global_settings.browse_current && current_track_path[0] != '\0') set_current_file(current_track_path); return 0; } update_track = false; } if (exit) { #ifdef HAVE_LCD_CHARCELLS status_set_record(false); status_set_audio(false); #endif if (global_settings.fade_on_stop) fade(0); lcd_stop_scroll(); bookmark_autobookmark(); audio_stop(); #ifdef AB_REPEAT_ENABLE ab_reset_markers(); #endif /* Keys can be locked when exiting, so either unlock here or implement key locking in tree.c too */ keys_locked=false; /* set dir browser to current playing song */ if (global_settings.browse_current && current_track_path[0] != '\0') set_current_file(current_track_path); return 0; } if ( button ) ata_spin(); if (restore && ((restoretimer == 0) || (restoretimer < current_tick))) { restore = false; restoretimer = 0; if (wps_display(id3, nid3)) { /* set dir browser to current playing song */ if (global_settings.browse_current && current_track_path[0] != '\0') set_current_file(current_track_path); return 0; } if (id3) wps_refresh(id3, nid3, 0, WPS_REFRESH_NON_STATIC); } if (button != BUTTON_NONE) lastbutton = button; } return 0; /* unreachable - just to reduce compiler warnings */ }