From b5442f8752f510f0b79d09579ad98ac517848721 Mon Sep 17 00:00:00 2001 From: Jonathan Gordon Date: Mon, 3 Aug 2009 04:57:43 +0000 Subject: these didnt get renamed for some reason git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22136 a1c6a512-1295-4272-9138-f99709370657 --- apps/gui/music_screen.c | 1250 ----------------------------------------------- apps/gui/music_screen.h | 37 -- apps/gui/wps.c | 1216 +++++++++++++++++++++++++++++++++++++++++++++ apps/gui/wps.h | 37 ++ 4 files changed, 1253 insertions(+), 1287 deletions(-) delete mode 100644 apps/gui/music_screen.c delete mode 100644 apps/gui/music_screen.h create mode 100644 apps/gui/wps.c create mode 100644 apps/gui/wps.h diff --git a/apps/gui/music_screen.c b/apps/gui/music_screen.c deleted file mode 100644 index d5eccd5868..0000000000 --- a/apps/gui/music_screen.c +++ /dev/null @@ -1,1250 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2002 Jerome Kuptz - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#include -#include -#include -#include "config.h" - -#include "system.h" -#include "file.h" -#include "lcd.h" -#include "font.h" -#include "backlight.h" -#include "action.h" -#include "kernel.h" -#include "filetypes.h" -#include "debug.h" -#include "sprintf.h" -#include "settings.h" -#include "wps_engine/wps_engine.h" -#include "mp3_playback.h" -#include "audio.h" -#include "usb.h" -#include "status.h" -#include "storage.h" -#include "screens.h" -#include "playlist.h" -#ifdef HAVE_LCD_BITMAP -#include "icons.h" -#include "peakmeter.h" -#endif -#include "lang.h" -#include "bookmark.h" -#include "misc.h" -#include "sound.h" -#include "onplay.h" -#include "abrepeat.h" -#include "playback.h" -#include "splash.h" -#include "cuesheet.h" -#include "ata_idle_notify.h" -#include "root_menu.h" -#include "backdrop.h" -#include "quickscreen.h" -#include "pitchscreen.h" -#include "appevents.h" -#include "viewport.h" -#include "pcmbuf.h" -#include "option_select.h" -#include "dsp.h" -#include "playlist_viewer.h" -#include "music_screen.h" - -#define RESTORE_WPS_INSTANTLY 0l -#define RESTORE_WPS_NEXT_SECOND ((long)(HZ+current_tick)) -/* in milliseconds */ -#define DEFAULT_SKIP_TRESH 3000ul - -static int wpsbars; -/* currently only one wps_state is needed */ -struct wps_state wps_state; -struct gui_wps gui_wps[NB_SCREENS]; -static struct wps_data wps_datas[NB_SCREENS]; - -/* initial setup of wps_data */ -static void wps_state_init(void); -static void track_changed_callback(void *param); -static void nextid3available_callback(void* param); - - - -#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 wps_fading_out = false; -void fade(bool fade_in, bool updatewps) -{ - int fp_global_vol = global_settings.volume << 8; - int fp_min_vol = sound_min(SOUND_VOLUME) << 8; - int fp_step = (fp_global_vol - fp_min_vol) / 30; - int i; - wps_fading_out = !fade_in; - if (fade_in) { - /* fade in */ - int fp_volume = fp_min_vol; - - /* zero out the sound */ - sound_set_volume(fp_min_vol >> 8); - - sleep(HZ/10); /* let audio thread run */ - audio_resume(); - - while (fp_volume < fp_global_vol - fp_step) { - fp_volume += fp_step; - sound_set_volume(fp_volume >> 8); - if (updatewps) - { - FOR_NB_SCREENS(i) - gui_wps_redraw(&gui_wps[i], 0, WPS_REFRESH_NON_STATIC); - } - sleep(1); - } - sound_set_volume(global_settings.volume); - } - else { - /* fade out */ - int fp_volume = fp_global_vol; - - while (fp_volume > fp_min_vol + fp_step) { - fp_volume -= fp_step; - sound_set_volume(fp_volume >> 8); - if (updatewps) - { - FOR_NB_SCREENS(i) - gui_wps_redraw(&gui_wps[i], 0, WPS_REFRESH_NON_STATIC); - } - sleep(1); - } - audio_pause(); - wps_fading_out = false; -#if CONFIG_CODEC != SWCODEC -#ifndef SIMULATOR - /* let audio thread run and wait for the mas to run out of data */ - while (!mp3_pause_done()) -#endif - sleep(HZ/10); -#endif - - /* reset volume to what it was before the fade */ - sound_set_volume(global_settings.volume); - } -} -bool is_wps_fading(void) -{ - return wps_fading_out; -} - -bool update_onvol_change(struct gui_wps * gwps) -{ - gui_wps_redraw(gwps, 0, WPS_REFRESH_NON_STATIC); - -#ifdef HAVE_LCD_CHARCELLS - splashf(0, "Vol: %3d dB", - sound_val2phys(SOUND_VOLUME, global_settings.volume)); - return true; -#endif - return false; -} - -bool ffwd_rew(int button) -{ - 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 */ - bool exit = false; - bool usb = false; - int i = 0; - const long ff_rw_accel = (global_settings.ff_rewind_accel + 3); - - if (button == ACTION_NONE) - { - status_set_ffmode(0); - return usb; - } - while (!exit) - { - switch ( button ) - { - case ACTION_WPS_SEEKFWD: - direction = 1; - case ACTION_WPS_SEEKBACK: - if (wps_state.ff_rewind) - { - if (direction == 1) - { - /* fast forwarding, calc max step relative to end */ - max_step = (wps_state.id3->length - - (wps_state.id3->elapsed + - ff_rewind_count)) * - FF_REWIND_MAX_PERCENT / 100; - } - else - { - /* rewinding, calc max step relative to start */ - max_step = (wps_state.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; - - /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */ - step += step >> ff_rw_accel; - } - else - { - if ( (audio_status() & AUDIO_STATUS_PLAY) && - wps_state.id3 && wps_state.id3->length ) - { - if (!wps_state.paused) -#if (CONFIG_CODEC == SWCODEC) - audio_pre_ff_rewind(); -#else - audio_pause(); -#endif -#if CONFIG_KEYPAD == PLAYER_PAD - FOR_NB_SCREENS(i) - gui_wps[i].display->stop_scroll(); -#endif - if (direction > 0) - status_set_ffmode(STATUS_FASTFORWARD); - else - status_set_ffmode(STATUS_FASTBACKWARD); - - wps_state.ff_rewind = true; - - step = 1000 * global_settings.ff_rewind_min_step; - } - else - break; - } - - if (direction > 0) { - if ((wps_state.id3->elapsed + ff_rewind_count) > - wps_state.id3->length) - ff_rewind_count = wps_state.id3->length - - wps_state.id3->elapsed; - } - else { - if ((int)(wps_state.id3->elapsed + ff_rewind_count) < 0) - ff_rewind_count = -wps_state.id3->elapsed; - } - - FOR_NB_SCREENS(i) - gui_wps_redraw(&gui_wps[i], - (wps_state.wps_time_countup == false)? - ff_rewind_count:-ff_rewind_count, - WPS_REFRESH_PLAYER_PROGRESS | - WPS_REFRESH_DYNAMIC); - - break; - - case ACTION_WPS_STOPSEEK: - wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count; - audio_ff_rewind(wps_state.id3->elapsed); - ff_rewind_count = 0; - wps_state.ff_rewind = false; - status_set_ffmode(0); -#if (CONFIG_CODEC != SWCODEC) - if (!wps_state.paused) - audio_resume(); -#endif -#ifdef HAVE_LCD_CHARCELLS - FOR_NB_SCREENS(i) - gui_wps_redraw(&gui_wps[i],0, WPS_REFRESH_ALL); -#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 = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_BLOCK); -#ifdef HAVE_TOUCHSCREEN - if (button == ACTION_TOUCHSCREEN) - button = wps_get_touchaction(gui_wps[SCREEN_MAIN].data); -#endif - } - } - return usb; -} - - -void display_keylock_text(bool locked) -{ - int i; - FOR_NB_SCREENS(i) - gui_wps[i].display->stop_scroll(); - - splash(HZ, locked ? ID2P(LANG_KEYLOCK_ON) : ID2P(LANG_KEYLOCK_OFF)); -} - - - - -#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD) -static void gwps_caption_backlight(struct wps_state *state) -{ - if (state && state->id3) - { -#ifdef HAVE_BACKLIGHT - if (global_settings.caption_backlight) - { - /* turn on backlight n seconds before track ends, and turn it off n - seconds into the new track. n == backlight_timeout, or 5s */ - int n = global_settings.backlight_timeout * 1000; - - if ( n < 1000 ) - n = 5000; /* use 5s if backlight is always on or off */ - - if (((state->id3->elapsed < 1000) || - ((state->id3->length - state->id3->elapsed) < (unsigned)n)) && - (state->paused == false)) - backlight_on(); - } -#endif -#ifdef HAVE_REMOTE_LCD - if (global_settings.remote_caption_backlight) - { - /* turn on remote backlight n seconds before track ends, and turn it - off n seconds into the new track. n == remote_backlight_timeout, - or 5s */ - int n = global_settings.remote_backlight_timeout * 1000; - - if ( n < 1000 ) - n = 5000; /* use 5s if backlight is always on or off */ - - if (((state->id3->elapsed < 1000) || - ((state->id3->length - state->id3->elapsed) < (unsigned)n)) && - (state->paused == false)) - remote_backlight_on(); - } -#endif - } -} -#endif - - -static void change_dir(int direction) -{ - if (global_settings.prevent_skip) - return; - - if (direction < 0) - audio_prev_dir(); - else if (direction > 0) - audio_next_dir(); - /* prevent the next dir to immediatly start being ffw'd */ - action_wait_for_release(); -} - -static void prev_track(unsigned long skip_thresh) -{ - if (wps_state.id3->elapsed < skip_thresh) - { - audio_prev(); - return; - } - else - { - if (wps_state.id3->cuesheet) - { - curr_cuesheet_skip(wps_state.id3->cuesheet, -1, wps_state.id3->elapsed); - return; - } - - if (!wps_state.paused) -#if (CONFIG_CODEC == SWCODEC) - audio_pre_ff_rewind(); -#else - audio_pause(); -#endif - - audio_ff_rewind(0); - -#if (CONFIG_CODEC != SWCODEC) - if (!wps_state.paused) - audio_resume(); -#endif - } -} - -static void next_track(void) -{ - /* take care of if we're playing a cuesheet */ - if (wps_state.id3->cuesheet) - { - if (curr_cuesheet_skip(wps_state.id3->cuesheet, 1, wps_state.id3->elapsed)) - { - /* if the result was false, then we really want - to skip to the next track */ - return; - } - } - - audio_next(); -} - -static void play_hop(int direction) -{ - unsigned long step = ((unsigned long)global_settings.skip_length)*1000; - unsigned long elapsed = wps_state.id3->elapsed; - unsigned long remaining = wps_state.id3->length - elapsed; - - if (!global_settings.prevent_skip && - (!step || - (direction > 0 && step >= remaining) || - (direction < 0 && elapsed < DEFAULT_SKIP_TRESH))) - { /* Do normal track skipping */ - if (direction > 0) - next_track(); - else if (direction < 0) - prev_track(DEFAULT_SKIP_TRESH); - return; - } - - if (direction == 1 && step >= remaining) - { -#if CONFIG_CODEC == SWCODEC - if(global_settings.beep) - pcmbuf_beep(1000, 150, 1500*global_settings.beep); -#endif - return; - } - else if ((direction == -1 && elapsed < step)) - { - elapsed = 0; - } - else - { - elapsed += step * direction; - } - if((audio_status() & AUDIO_STATUS_PLAY) && !wps_state.paused) - { -#if (CONFIG_CODEC == SWCODEC) - audio_pre_ff_rewind(); -#else - audio_pause(); -#endif - } - audio_ff_rewind(wps_state.id3->elapsed = elapsed); -#if (CONFIG_CODEC != SWCODEC) - if (!wps_state.paused) - audio_resume(); -#endif -} - -static void gwps_fix_statusbars(void) -{ -#ifdef HAVE_LCD_BITMAP - int i; - wpsbars = VP_SB_HIDE_ALL; - FOR_NB_SCREENS(i) - { - bool draw = false; - if (gui_wps[i].data->wps_sb_tag) - draw = gui_wps[i].data->show_sb_on_wps; - else if (statusbar_position(i) != STATUSBAR_OFF) - draw = true; - if (draw) - wpsbars |= (VP_SB_ONSCREEN(i) | VP_SB_IGNORE_SETTING(i)); - } -#else - wpsbars = VP_SB_ALLSCREENS; -#endif -} - -#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) -/* - * If the user is unable to see the wps, because the display is deactivated, - * we suppress updates until the wps is activated again (the lcd driver will - * call this hook to issue an instant update) - * */ -static void wps_lcd_activation_hook(void) -{ - wps_state.do_full_update = true; - /* force timeout in wps main loop, so that the update is instantly */ - queue_post(&button_queue, BUTTON_NONE, 0); -} -#endif - -static void gwps_leave_wps(void) -{ - int i, oldbars = VP_SB_HIDE_ALL; - - FOR_NB_SCREENS(i) - gui_wps[i].display->stop_scroll(); - if (global_settings.statusbar) - oldbars = VP_SB_ALLSCREENS; - -#if LCD_DEPTH > 1 - show_main_backdrop(); -#endif -#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 - show_remote_main_backdrop(); -#endif - viewportmanager_set_statusbar(oldbars); -#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) - /* Play safe and unregister the hook */ - lcd_activation_set_hook(NULL); -#endif -} - -void gwps_draw_statusbars(void) -{ - viewportmanager_set_statusbar(wpsbars); -} -#ifdef HAVE_TOUCHSCREEN -int wps_get_touchaction(struct wps_data *data) -{ - short x,y; - short vx, vy; - int type = action_get_touchscreen_press(&x, &y); - int i; - static int last_action = ACTION_NONE; - struct touchregion *r; - bool repeated = (type == BUTTON_REPEAT); - bool released = (type == BUTTON_REL); - for (i=0; itouchregion_count; i++) - { - r = &data->touchregion[i]; - /* make sure this region's viewport is visible */ - if (r->wvp->hidden_flags&VP_DRAW_HIDDEN) - continue; - /* reposition the touch inside the viewport */ - vx = x - r->wvp->vp.x; - vy = y - r->wvp->vp.y; - /* check if it's inside this viewport */ - if (vx >= 0 && vx < r->wvp->vp.x + r->wvp->vp.width && - vy >= 0 && vy < r->wvp->vp.y + r->wvp->vp.height) - { - /* now see if the point is inside this region */ - if (vx >= r->x && vx < r->x+r->width && - vy >= r->y && vy < r->y+r->height) - { - /* reposition the touch within the area */ - vx -= r->x; - vy -= r->y; - - switch(r->type) - { - case WPS_TOUCHREGION_ACTION: - if ((repeated && r->repeat) || (released && !r->repeat)) - { - last_action = r->action; - return r->action; - } - break; - case WPS_TOUCHREGION_SCROLLBAR: - if(r->width > r->height) - /* landscape */ - wps_state.id3->elapsed = (vx * - wps_state.id3->length) / r->width; - else - /* portrait */ - wps_state.id3->elapsed = (vy * - wps_state.id3->length) / r->height; - - audio_ff_rewind(wps_state.id3->elapsed); - break; - case WPS_TOUCHREGION_VOLUME: - { - const int min_vol = sound_min(SOUND_VOLUME); - const int max_vol = sound_max(SOUND_VOLUME); - if(r->width > r->height) - /* landscape */ - global_settings.volume = (vx * - (max_vol - min_vol)) / r->width; - else - /* portrait */ - global_settings.volume = (vy * - (max_vol-min_vol)) / r->height; - - global_settings.volume += min_vol; - setvol(); - break; - } - } - } - } - } - if ((last_action == ACTION_WPS_SEEKBACK || last_action == ACTION_WPS_SEEKFWD)) - return ACTION_WPS_STOPSEEK; - last_action = ACTION_TOUCHSCREEN; - return ACTION_TOUCHSCREEN; -} -#endif -/* The WPS can be left in two ways: - * a) call a function, which draws over the wps. In this case, the wps - * will be still active (i.e. the below function didn't return) - * b) return with a value evaluated by root_menu.c, in this case the wps - * is really left, and root_menu will handle the next screen - * - * In either way, call gwps_leave_wps(), in order to restore the correct - * "main screen" backdrops and statusbars - */ -long gui_wps_show(void) -{ - long button = 0; - bool restore = true; - long restoretimer = RESTORE_WPS_INSTANTLY; /* timer to delay screen redraw temporarily */ - bool exit = false; - bool bookmark = false; - bool update = false; - int i; - long last_left = 0, last_right = 0; - -#ifdef HAVE_LCD_CHARCELLS - status_set_audio(true); - status_set_param(false); -#endif - -#ifdef AB_REPEAT_ENABLE - ab_repeat_init(); - ab_reset_markers(); -#endif - wps_state_init(); - - while ( 1 ) - { - bool audio_paused = (audio_status() & AUDIO_STATUS_PAUSE)?true:false; - - /* did someone else (i.e power thread) change audio pause mode? */ - if (wps_state.paused != audio_paused) { - wps_state.paused = audio_paused; - - /* if another thread paused audio, we are probably in car mode, - about to shut down. lets save the settings. */ - if (wps_state.paused) { - settings_save(); -#if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF) - call_storage_idle_notifys(true); -#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 */ - bool pm=false; - FOR_NB_SCREENS(i) - { - if(gui_wps[i].data->peak_meter_enabled) - pm = true; - } - - if (pm) { - 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 = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_NOBLOCK); - if (button != ACTION_NONE) { - break; - } - peak_meter_peek(); - sleep(0); /* Sleep until end of current tick. */ - - if (TIME_AFTER(current_tick, next_refresh)) { - FOR_NB_SCREENS(i) - { - if(gui_wps[i].data->peak_meter_enabled) - gui_wps_redraw(&gui_wps[i], 0, - WPS_REFRESH_PEAK_METER); - next_refresh += HZ / PEAK_METER_FPS; - } - } - } - - } - - /* The peak meter is disabled - -> no additional screen updates needed */ - else -#endif - { - button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK, - restore ? HZ/100 : HZ/5); - } - - /* Exit if audio has stopped playing. This happens e.g. at end of - playlist or if using the sleep timer. */ - if (!(audio_status() & AUDIO_STATUS_PLAY)) - exit = true; -#ifdef HAVE_TOUCHSCREEN - if (button == ACTION_TOUCHSCREEN) - button = wps_get_touchaction(gui_wps[SCREEN_MAIN].data); -#endif -/* The iPods/X5/M5 use a single button for the A-B mode markers, - defined as ACTION_WPSAB_SINGLE in their config files. */ -#ifdef ACTION_WPSAB_SINGLE - if (!global_settings.party_mode && ab_repeat_mode_enabled()) - { - static int wps_ab_state = 0; - if (button == ACTION_WPSAB_SINGLE) - { - switch (wps_ab_state) - { - case 0: /* set the A spot */ - button = ACTION_WPS_ABSETA_PREVDIR; - break; - case 1: /* set the B spot */ - button = ACTION_WPS_ABSETB_NEXTDIR; - break; - case 2: - button = ACTION_WPS_ABRESET; - break; - } - wps_ab_state = (wps_ab_state+1) % 3; - } - } -#endif - switch(button) - { - case ACTION_WPS_CONTEXT: - { - gwps_leave_wps(); - /* if music is stopped in the context menu we want to exit the wps */ - if (onplay(wps_state.id3->path, - FILE_ATTR_AUDIO, CONTEXT_WPS) == ONPLAY_MAINMENU - || !audio_status()) - return GO_TO_ROOT; - restore = true; - } - break; - - case ACTION_WPS_BROWSE: -#ifdef HAVE_LCD_CHARCELLS - status_set_record(false); - status_set_audio(false); -#endif - gwps_leave_wps(); - return GO_TO_PREVIOUS_BROWSER; - break; - - /* play/pause */ - case ACTION_WPS_PLAY: - if (global_settings.party_mode) - break; - if ( wps_state.paused ) - { - wps_state.paused = false; - if ( global_settings.fade_on_stop ) - fade(true, true); - else - audio_resume(); - } - else - { - wps_state.paused = true; - if ( global_settings.fade_on_stop ) - fade(false, true); - else - audio_pause(); - settings_save(); -#if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF) - call_storage_idle_notifys(true); /* make sure resume info is saved */ -#endif - } - break; - - case ACTION_WPS_VOLUP: - { - FOR_NB_SCREENS(i) - gui_wps[i].data->button_time_volume = current_tick; - global_settings.volume++; - bool res = false; - setvol(); - FOR_NB_SCREENS(i) - { - if(update_onvol_change(&gui_wps[i])) - res = true; - } - if (res) { - restore = true; - restoretimer = RESTORE_WPS_NEXT_SECOND; - } - } - break; - case ACTION_WPS_VOLDOWN: - { - FOR_NB_SCREENS(i) - gui_wps[i].data->button_time_volume = current_tick; - global_settings.volume--; - setvol(); - bool res = false; - FOR_NB_SCREENS(i) - { - if(update_onvol_change(&gui_wps[i])) - res = true; - } - if (res) { - restore = true; - restoretimer = RESTORE_WPS_NEXT_SECOND; - } - } - break; - /* fast forward - OR next dir if this is straight after ACTION_WPS_SKIPNEXT */ - case ACTION_WPS_SEEKFWD: - if (global_settings.party_mode) - break; - if (current_tick -last_right < HZ) - { - if (wps_state.id3->cuesheet) - { - audio_next(); - } - else - { - change_dir(1); - } - } - else - ffwd_rew(ACTION_WPS_SEEKFWD); - last_right = last_left = 0; - break; - /* fast rewind - OR prev dir if this is straight after ACTION_WPS_SKIPPREV,*/ - case ACTION_WPS_SEEKBACK: - if (global_settings.party_mode) - break; - if (current_tick -last_left < HZ) - { - if (wps_state.id3->cuesheet) - { - if (!wps_state.paused) -#if (CONFIG_CODEC == SWCODEC) - audio_pre_ff_rewind(); -#else - audio_pause(); -#endif - audio_ff_rewind(0); - } - else - { - change_dir(-1); - } - } - else - ffwd_rew(ACTION_WPS_SEEKBACK); - last_left = last_right = 0; - break; - - /* prev / restart */ - case ACTION_WPS_SKIPPREV: - if (global_settings.party_mode) - break; - last_left = current_tick; -#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() ) - { - if ( ab_after_A_marker(wps_state.id3->elapsed) ) - { - ab_jump_to_A_marker(); - break; -#if (AB_REPEAT_ENABLE == 2) - } else { - ab_reset_markers(); -#endif - } - } - else - /* ...otherwise, do it normally */ -#endif - play_hop(-1); - break; - - /* next - OR if skip length set, hop by predetermined amount. */ - case ACTION_WPS_SKIPNEXT: - if (global_settings.party_mode) - break; - last_right = current_tick; -#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() ) - { - if ( ab_before_A_marker(wps_state.id3->elapsed) ) - { - ab_jump_to_A_marker(); - break; -#if (AB_REPEAT_ENABLE == 2) - } else { - ab_reset_markers(); -#endif - } - } - else - /* ...otherwise, do it normally */ -#endif - play_hop(1); - break; - /* next / prev directories */ - /* and set A-B markers if in a-b mode */ - case ACTION_WPS_ABSETB_NEXTDIR: - if (global_settings.party_mode) - break; -#if defined(AB_REPEAT_ENABLE) - if (ab_repeat_mode_enabled()) - { - ab_set_B_marker(wps_state.id3->elapsed); - ab_jump_to_A_marker(); - } - else -#endif - { - change_dir(1); - } - break; - case ACTION_WPS_ABSETA_PREVDIR: - if (global_settings.party_mode) - break; -#if defined(AB_REPEAT_ENABLE) - if (ab_repeat_mode_enabled()) - ab_set_A_marker(wps_state.id3->elapsed); - else -#endif - { - change_dir(-1); - } - break; - /* menu key functions */ - case ACTION_WPS_MENU: - gwps_leave_wps(); - return GO_TO_ROOT; - break; - - -#ifdef HAVE_QUICKSCREEN - case ACTION_WPS_QUICKSCREEN: - { - gwps_leave_wps(); - if (quick_screen_quick(button)) - return SYS_USB_CONNECTED; - restore = true; - } - break; -#endif /* HAVE_QUICKSCREEN */ - - /* screen settings */ -#ifdef BUTTON_F3 - case ACTION_F3: - { - gwps_leave_wps(); - if (quick_screen_f3(BUTTON_F3)) - return SYS_USB_CONNECTED; - restore = true; - } - break; -#endif /* BUTTON_F3 */ - - /* pitch screen */ -#ifdef HAVE_PITCHSCREEN - case ACTION_WPS_PITCHSCREEN: - { - gwps_leave_wps(); - if (1 == gui_syncpitchscreen_run()) - return SYS_USB_CONNECTED; - restore = true; - } - break; -#endif /* HAVE_PITCHSCREEN */ - -#ifdef AB_REPEAT_ENABLE - /* reset A&B markers */ - case ACTION_WPS_ABRESET: - if (ab_repeat_mode_enabled()) - { - ab_reset_markers(); - update = true; - } - break; -#endif /* AB_REPEAT_ENABLE */ - - /* stop and exit wps */ - case ACTION_WPS_STOP: - if (global_settings.party_mode) - break; - bookmark = true; - exit = true; - break; - - case ACTION_WPS_ID3SCREEN: - { - gwps_leave_wps(); - browse_id3(); - restore = true; - } - break; -#ifdef HAVE_TOUCHSCREEN - case ACTION_TOUCH_SHUFFLE: /* toggle shuffle mode */ - { - global_settings.playlist_shuffle = - !global_settings.playlist_shuffle; -#if CONFIG_CODEC == SWCODEC - dsp_set_replaygain(); -#endif - if (global_settings.playlist_shuffle) - playlist_randomise(NULL, current_tick, true); - else - playlist_sort(NULL, true); - } - break; - case ACTION_TOUCH_REPMODE: /* cycle the repeat mode setting */ - { - const struct settings_list *rep_setting = - find_setting(&global_settings.repeat_mode, NULL); - option_select_next_val(rep_setting, false, true); - audio_flush_and_reload_tracks(); - } - break; -#endif /* HAVE_TOUCHSCREEN */ - /* this case is used by the softlock feature - * it requests a full update here */ - case ACTION_REDRAW: - wps_state.do_full_update = true; - break; - case ACTION_NONE: /* Timeout, do a partial update */ - update = true; - ffwd_rew(button); /* hopefully fix the ffw/rwd bug */ - break; -#ifdef HAVE_RECORDING - case ACTION_WPS_REC: - exit = true; - break; -#endif - case SYS_POWEROFF: - default_event_handler(SYS_POWEROFF); - break; - case ACTION_WPS_VIEW_PLAYLIST: - gwps_leave_wps(); - if (playlist_viewer()) /* true if USB connected */ - return SYS_USB_CONNECTED; - restore = true; - break; - default: - if(default_event_handler(button) == SYS_USB_CONNECTED) - return GO_TO_ROOT; - update = true; - break; - } - - if (wps_state.do_full_update || update) - { -#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD) - gwps_caption_backlight(&wps_state); -#endif - FOR_NB_SCREENS(i) - { -#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) - if (lcd_active() -#ifdef HAVE_REMOTE_LCD - /* currently, all remotes are readable without backlight - * so still update those */ - || (i == SCREEN_REMOTE) -#endif - ) -#endif - { - gui_wps_update(&gui_wps[i]); - } - } - wps_state.do_full_update = false; - update = false; - } - - if (restore && wps_state.id3 && - ((restoretimer == RESTORE_WPS_INSTANTLY) || - TIME_AFTER(current_tick, restoretimer))) - { - restore = false; - restoretimer = RESTORE_WPS_INSTANTLY; - gwps_fix_statusbars(); -#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) - lcd_activation_set_hook(wps_lcd_activation_hook); -#endif - FOR_NB_SCREENS(i) - { - screens[i].stop_scroll(); - gui_wps_display(&gui_wps[i]); - } - } - - if (exit) { -#ifdef HAVE_LCD_CHARCELLS - status_set_record(false); - status_set_audio(false); -#endif - if (global_settings.fade_on_stop) - fade(false, true); - - if (bookmark) - bookmark_autobookmark(); - audio_stop(); -#ifdef AB_REPEAT_ENABLE - ab_reset_markers(); -#endif - gwps_leave_wps(); -#ifdef HAVE_RECORDING - if (button == ACTION_WPS_REC) - return GO_TO_RECSCREEN; -#endif - if (global_settings.browse_current) - return GO_TO_PREVIOUS_BROWSER; - return GO_TO_PREVIOUS; - } - - if (button && !IS_SYSEVENT(button) ) - storage_spin(); - } - return GO_TO_ROOT; /* unreachable - just to reduce compiler warnings */ -} - -/* this is called from the playback thread so NO DRAWING! */ -static void track_changed_callback(void *param) -{ - wps_state.id3 = (struct mp3entry*)param; - wps_state.nid3 = audio_next_track(); - if (wps_state.id3->cuesheet) - { - cue_find_current_track(wps_state.id3->cuesheet, wps_state.id3->elapsed); - cue_spoof_id3(wps_state.id3->cuesheet, wps_state.id3); - } - wps_state.do_full_update = true; -} -static void nextid3available_callback(void* param) -{ - (void)param; - wps_state.nid3 = audio_next_track(); - wps_state.do_full_update = true; -} - - -static void wps_state_init(void) -{ - wps_state.ff_rewind = false; - wps_state.paused = false; - if(audio_status() & AUDIO_STATUS_PLAY) - { - wps_state.id3 = audio_current_track(); - wps_state.nid3 = audio_next_track(); - } - else - { - wps_state.id3 = NULL; - wps_state.nid3 = NULL; - } - /* We'll be updating due to restore initialized with true */ - wps_state.do_full_update = false; - /* add the WPS track event callbacks */ - add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, track_changed_callback); - add_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, false, nextid3available_callback); -} - - -#ifdef HAVE_LCD_BITMAP -static void statusbar_toggle_handler(void *data) -{ - (void)data; - int i; - gwps_fix_statusbars(); - - FOR_NB_SCREENS(i) - { - struct viewport *vp = &gui_wps[i].data->viewports[0].vp; - bool draw = wpsbars & (VP_SB_ONSCREEN(i) | VP_SB_IGNORE_SETTING(i)); - if (!draw) - { - vp->y = 0; - vp->height = screens[i].lcdheight; - } - else - { - bool bar_at_top = statusbar_position(i) != STATUSBAR_BOTTOM; - vp->y = bar_at_top?STATUSBAR_HEIGHT:0; - vp->height = screens[i].lcdheight - STATUSBAR_HEIGHT; - } - } -} -#endif - -void gui_sync_wps_init(void) -{ - int i; - FOR_NB_SCREENS(i) - { - wps_data_init(&wps_datas[i]); -#ifdef HAVE_ALBUMART - wps_datas[i].wps_uses_albumart = 0; -#endif -#ifdef HAVE_REMOTE_LCD - wps_datas[i].remote_wps = (i != 0); -#endif - gui_wps[i].data = &wps_datas[i]; - gui_wps[i].display = &screens[i]; - /* Currently no seperate wps_state needed/possible - so use the only available ( "global" ) one */ - gui_wps[i].state = &wps_state; - } -#ifdef HAVE_LCD_BITMAP - add_event(GUI_EVENT_STATUSBAR_TOGGLE, false, statusbar_toggle_handler); -#endif -#if LCD_DEPTH > 1 - unload_wps_backdrop(); -#endif -#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 - unload_remote_wps_backdrop(); -#endif -} - -#ifdef HAVE_ALBUMART -/* Returns true if at least one of the gui_wps screens has an album art - tag in its wps structure */ -bool gui_sync_wps_uses_albumart(void) -{ - int i; - FOR_NB_SCREENS(i) { - struct gui_wps *gwps = &gui_wps[i]; - if (gwps->data && (gwps->data->wps_uses_albumart != WPS_ALBUMART_NONE)) - return true; - } - return false; -} -#endif diff --git a/apps/gui/music_screen.h b/apps/gui/music_screen.h deleted file mode 100644 index 90614cb65b..0000000000 --- a/apps/gui/music_screen.h +++ /dev/null @@ -1,37 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: gwps-common.h 20492 2009-03-23 17:19:48Z alle $ - * - * Copyright (C) 2002 Björn Stenberg - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#ifndef _MUSICSCREEN_H_ -#define _MUSICSCREEN_H_ -#include - -long gui_wps_show(void); - - -void gui_sync_wps_init(void); - -/* fades the volume, e.g. on pause or stop */ -void fade(bool fade_in, bool updatewps); - -bool ffwd_rew(int button); -void display_keylock_text(bool locked); - -bool is_wps_fading(void); -#endif diff --git a/apps/gui/wps.c b/apps/gui/wps.c new file mode 100644 index 0000000000..4a7de875d2 --- /dev/null +++ b/apps/gui/wps.c @@ -0,0 +1,1216 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 Jerome Kuptz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include +#include "config.h" + +#include "system.h" +#include "file.h" +#include "lcd.h" +#include "font.h" +#include "backlight.h" +#include "action.h" +#include "kernel.h" +#include "filetypes.h" +#include "debug.h" +#include "sprintf.h" +#include "settings.h" +#include "skin_engine/skin_engine.h" +#include "mp3_playback.h" +#include "audio.h" +#include "usb.h" +#include "status.h" +#include "storage.h" +#include "screens.h" +#include "playlist.h" +#ifdef HAVE_LCD_BITMAP +#include "icons.h" +#include "peakmeter.h" +#endif +#include "lang.h" +#include "bookmark.h" +#include "misc.h" +#include "sound.h" +#include "onplay.h" +#include "abrepeat.h" +#include "playback.h" +#include "splash.h" +#include "cuesheet.h" +#include "ata_idle_notify.h" +#include "root_menu.h" +#include "backdrop.h" +#include "quickscreen.h" +#include "pitchscreen.h" +#include "appevents.h" +#include "viewport.h" +#include "pcmbuf.h" +#include "option_select.h" +#include "dsp.h" +#include "playlist_viewer.h" +#include "wps.h" + +#define RESTORE_WPS_INSTANTLY 0l +#define RESTORE_WPS_NEXT_SECOND ((long)(HZ+current_tick)) +/* in milliseconds */ +#define DEFAULT_SKIP_TRESH 3000ul + +static int wpsbars; +/* currently only one wps_state is needed */ +struct wps_state wps_state; +struct gui_wps gui_wps[NB_SCREENS]; +static struct wps_data wps_datas[NB_SCREENS]; + +/* initial setup of wps_data */ +static void wps_state_init(void); +static void track_changed_callback(void *param); +static void nextid3available_callback(void* param); + + + +#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 wps_fading_out = false; +void fade(bool fade_in, bool updatewps) +{ + int fp_global_vol = global_settings.volume << 8; + int fp_min_vol = sound_min(SOUND_VOLUME) << 8; + int fp_step = (fp_global_vol - fp_min_vol) / 30; + int i; + wps_fading_out = !fade_in; + if (fade_in) { + /* fade in */ + int fp_volume = fp_min_vol; + + /* zero out the sound */ + sound_set_volume(fp_min_vol >> 8); + + sleep(HZ/10); /* let audio thread run */ + audio_resume(); + + while (fp_volume < fp_global_vol - fp_step) { + fp_volume += fp_step; + sound_set_volume(fp_volume >> 8); + if (updatewps) + { + FOR_NB_SCREENS(i) + skin_update(&gui_wps[i], WPS_REFRESH_NON_STATIC); + } + sleep(1); + } + sound_set_volume(global_settings.volume); + } + else { + /* fade out */ + int fp_volume = fp_global_vol; + + while (fp_volume > fp_min_vol + fp_step) { + fp_volume -= fp_step; + sound_set_volume(fp_volume >> 8); + if (updatewps) + { + FOR_NB_SCREENS(i) + skin_update(&gui_wps[i], WPS_REFRESH_NON_STATIC); + } + sleep(1); + } + audio_pause(); + wps_fading_out = false; +#if CONFIG_CODEC != SWCODEC +#ifndef SIMULATOR + /* let audio thread run and wait for the mas to run out of data */ + while (!mp3_pause_done()) +#endif + sleep(HZ/10); +#endif + + /* reset volume to what it was before the fade */ + sound_set_volume(global_settings.volume); + } +} +bool is_wps_fading(void) +{ + return wps_fading_out; +} + +bool update_onvol_change(struct gui_wps * gwps) +{ + skin_update(gwps, WPS_REFRESH_NON_STATIC); + +#ifdef HAVE_LCD_CHARCELLS + splashf(0, "Vol: %3d dB", + sound_val2phys(SOUND_VOLUME, global_settings.volume)); + return true; +#endif + return false; +} + +bool ffwd_rew(int button) +{ + 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 */ + bool exit = false; + bool usb = false; + int i = 0; + const long ff_rw_accel = (global_settings.ff_rewind_accel + 3); + + if (button == ACTION_NONE) + { + status_set_ffmode(0); + return usb; + } + while (!exit) + { + switch ( button ) + { + case ACTION_WPS_SEEKFWD: + direction = 1; + case ACTION_WPS_SEEKBACK: + if (wps_state.ff_rewind) + { + if (direction == 1) + { + /* fast forwarding, calc max step relative to end */ + max_step = (wps_state.id3->length - + (wps_state.id3->elapsed + + ff_rewind_count)) * + FF_REWIND_MAX_PERCENT / 100; + } + else + { + /* rewinding, calc max step relative to start */ + max_step = (wps_state.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; + + /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */ + step += step >> ff_rw_accel; + } + else + { + if ( (audio_status() & AUDIO_STATUS_PLAY) && + wps_state.id3 && wps_state.id3->length ) + { + if (!wps_state.paused) +#if (CONFIG_CODEC == SWCODEC) + audio_pre_ff_rewind(); +#else + audio_pause(); +#endif +#if CONFIG_KEYPAD == PLAYER_PAD + FOR_NB_SCREENS(i) + gui_wps[i].display->stop_scroll(); +#endif + if (direction > 0) + status_set_ffmode(STATUS_FASTFORWARD); + else + status_set_ffmode(STATUS_FASTBACKWARD); + + wps_state.ff_rewind = true; + + step = 1000 * global_settings.ff_rewind_min_step; + } + else + break; + } + + if (direction > 0) { + if ((wps_state.id3->elapsed + ff_rewind_count) > + wps_state.id3->length) + ff_rewind_count = wps_state.id3->length - + wps_state.id3->elapsed; + } + else { + if ((int)(wps_state.id3->elapsed + ff_rewind_count) < 0) + ff_rewind_count = -wps_state.id3->elapsed; + } + + /* set the wps state ff_rewind_count so the progess info + displays corectly */ + wps_state.ff_rewind_count = (wps_state.wps_time_countup == false)? + ff_rewind_count:-ff_rewind_count; + FOR_NB_SCREENS(i) + { + skin_update(&gui_wps[i], + WPS_REFRESH_PLAYER_PROGRESS | + WPS_REFRESH_DYNAMIC); + } + + break; + + case ACTION_WPS_STOPSEEK: + wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count; + audio_ff_rewind(wps_state.id3->elapsed); + wps_state.ff_rewind_count = 0; + wps_state.ff_rewind = false; + status_set_ffmode(0); +#if (CONFIG_CODEC != SWCODEC) + if (!wps_state.paused) + audio_resume(); +#endif +#ifdef HAVE_LCD_CHARCELLS + FOR_NB_SCREENS(i) + skin_update(&gui_wps[i], WPS_REFRESH_ALL); +#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 = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_BLOCK); +#ifdef HAVE_TOUCHSCREEN + if (button == ACTION_TOUCHSCREEN) + button = wps_get_touchaction(gui_wps[SCREEN_MAIN].data); +#endif + } + } + return usb; +} + + +void display_keylock_text(bool locked) +{ + int i; + FOR_NB_SCREENS(i) + gui_wps[i].display->stop_scroll(); + + splash(HZ, locked ? ID2P(LANG_KEYLOCK_ON) : ID2P(LANG_KEYLOCK_OFF)); +} + + + + +#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD) +static void gwps_caption_backlight(struct wps_state *state) +{ + if (state && state->id3) + { +#ifdef HAVE_BACKLIGHT + if (global_settings.caption_backlight) + { + /* turn on backlight n seconds before track ends, and turn it off n + seconds into the new track. n == backlight_timeout, or 5s */ + int n = global_settings.backlight_timeout * 1000; + + if ( n < 1000 ) + n = 5000; /* use 5s if backlight is always on or off */ + + if (((state->id3->elapsed < 1000) || + ((state->id3->length - state->id3->elapsed) < (unsigned)n)) && + (state->paused == false)) + backlight_on(); + } +#endif +#ifdef HAVE_REMOTE_LCD + if (global_settings.remote_caption_backlight) + { + /* turn on remote backlight n seconds before track ends, and turn it + off n seconds into the new track. n == remote_backlight_timeout, + or 5s */ + int n = global_settings.remote_backlight_timeout * 1000; + + if ( n < 1000 ) + n = 5000; /* use 5s if backlight is always on or off */ + + if (((state->id3->elapsed < 1000) || + ((state->id3->length - state->id3->elapsed) < (unsigned)n)) && + (state->paused == false)) + remote_backlight_on(); + } +#endif + } +} +#endif + + +static void change_dir(int direction) +{ + if (global_settings.prevent_skip) + return; + + if (direction < 0) + audio_prev_dir(); + else if (direction > 0) + audio_next_dir(); + /* prevent the next dir to immediatly start being ffw'd */ + action_wait_for_release(); +} + +static void prev_track(unsigned long skip_thresh) +{ + if (wps_state.id3->elapsed < skip_thresh) + { + audio_prev(); + return; + } + else + { + if (wps_state.id3->cuesheet) + { + curr_cuesheet_skip(wps_state.id3->cuesheet, -1, wps_state.id3->elapsed); + return; + } + + if (!wps_state.paused) +#if (CONFIG_CODEC == SWCODEC) + audio_pre_ff_rewind(); +#else + audio_pause(); +#endif + + audio_ff_rewind(0); + +#if (CONFIG_CODEC != SWCODEC) + if (!wps_state.paused) + audio_resume(); +#endif + } +} + +static void next_track(void) +{ + /* take care of if we're playing a cuesheet */ + if (wps_state.id3->cuesheet) + { + if (curr_cuesheet_skip(wps_state.id3->cuesheet, 1, wps_state.id3->elapsed)) + { + /* if the result was false, then we really want + to skip to the next track */ + return; + } + } + + audio_next(); +} + +static void play_hop(int direction) +{ + unsigned long step = ((unsigned long)global_settings.skip_length)*1000; + unsigned long elapsed = wps_state.id3->elapsed; + unsigned long remaining = wps_state.id3->length - elapsed; + + if (!global_settings.prevent_skip && + (!step || + (direction > 0 && step >= remaining) || + (direction < 0 && elapsed < DEFAULT_SKIP_TRESH))) + { /* Do normal track skipping */ + if (direction > 0) + next_track(); + else if (direction < 0) + prev_track(DEFAULT_SKIP_TRESH); + return; + } + + if (direction == 1 && step >= remaining) + { +#if CONFIG_CODEC == SWCODEC + if(global_settings.beep) + pcmbuf_beep(1000, 150, 1500*global_settings.beep); +#endif + return; + } + else if ((direction == -1 && elapsed < step)) + { + elapsed = 0; + } + else + { + elapsed += step * direction; + } + if((audio_status() & AUDIO_STATUS_PLAY) && !wps_state.paused) + { +#if (CONFIG_CODEC == SWCODEC) + audio_pre_ff_rewind(); +#else + audio_pause(); +#endif + } + audio_ff_rewind(wps_state.id3->elapsed = elapsed); +#if (CONFIG_CODEC != SWCODEC) + if (!wps_state.paused) + audio_resume(); +#endif +} + +static void gwps_fix_statusbars(void) +{ +#ifdef HAVE_LCD_BITMAP + int i; + wpsbars = VP_SB_HIDE_ALL; + FOR_NB_SCREENS(i) + { + bool draw = false; + if (gui_wps[i].data->wps_sb_tag) + draw = gui_wps[i].data->show_sb_on_wps; + else if (statusbar_position(i) != STATUSBAR_OFF) + draw = true; + if (draw) + wpsbars |= (VP_SB_ONSCREEN(i) | VP_SB_IGNORE_SETTING(i)); + } +#else + wpsbars = VP_SB_ALLSCREENS; +#endif +} + +#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) +/* + * If the user is unable to see the wps, because the display is deactivated, + * we suppress updates until the wps is activated again (the lcd driver will + * call this hook to issue an instant update) + * */ +static void wps_lcd_activation_hook(void) +{ + wps_state.do_full_update = true; + /* force timeout in wps main loop, so that the update is instantly */ + queue_post(&button_queue, BUTTON_NONE, 0); +} +#endif + +static void gwps_leave_wps(void) +{ + int i, oldbars = VP_SB_HIDE_ALL; + + FOR_NB_SCREENS(i) + gui_wps[i].display->stop_scroll(); + if (global_settings.statusbar) + oldbars = VP_SB_ALLSCREENS; + +#if LCD_DEPTH > 1 + show_main_backdrop(); +#endif +#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 + show_remote_main_backdrop(); +#endif + viewportmanager_set_statusbar(oldbars); +#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) + /* Play safe and unregister the hook */ + lcd_activation_set_hook(NULL); +#endif +} + +void gwps_draw_statusbars(void) +{ + viewportmanager_set_statusbar(wpsbars); +} +#ifdef HAVE_TOUCHSCREEN +int wps_get_touchaction(struct wps_data *data) +{ + short x,y; + short vx, vy; + int type = action_get_touchscreen_press(&x, &y); + int i; + static int last_action = ACTION_NONE; + struct touchregion *r; + bool repeated = (type == BUTTON_REPEAT); + bool released = (type == BUTTON_REL); + for (i=0; itouchregion_count; i++) + { + r = &data->touchregion[i]; + /* make sure this region's viewport is visible */ + if (r->wvp->hidden_flags&VP_DRAW_HIDDEN) + continue; + /* reposition the touch inside the viewport */ + vx = x - r->wvp->vp.x; + vy = y - r->wvp->vp.y; + /* check if it's inside this viewport */ + if (vx >= 0 && vx < r->wvp->vp.x + r->wvp->vp.width && + vy >= 0 && vy < r->wvp->vp.y + r->wvp->vp.height) + { + /* now see if the point is inside this region */ + if (vx >= r->x && vx < r->x+r->width && + vy >= r->y && vy < r->y+r->height) + { + if ((repeated && r->repeat) || + (released && !r->repeat)) + { + last_action = r->action; + return r->action; + } + } + } + } + if ((last_action == ACTION_WPS_SEEKBACK || last_action == ACTION_WPS_SEEKFWD)) + return ACTION_WPS_STOPSEEK; + last_action = ACTION_TOUCHSCREEN; + return ACTION_TOUCHSCREEN; +} +#endif +/* The WPS can be left in two ways: + * a) call a function, which draws over the wps. In this case, the wps + * will be still active (i.e. the below function didn't return) + * b) return with a value evaluated by root_menu.c, in this case the wps + * is really left, and root_menu will handle the next screen + * + * In either way, call gwps_leave_wps(), in order to restore the correct + * "main screen" backdrops and statusbars + */ +long gui_wps_show(void) +{ + long button = 0; + bool restore = true; + long restoretimer = RESTORE_WPS_INSTANTLY; /* timer to delay screen redraw temporarily */ + bool exit = false; + bool bookmark = false; + bool update = false; + int i; + long last_left = 0, last_right = 0; + +#ifdef HAVE_LCD_CHARCELLS + status_set_audio(true); + status_set_param(false); +#endif + +#ifdef AB_REPEAT_ENABLE + ab_repeat_init(); + ab_reset_markers(); +#endif + wps_state_init(); + + while ( 1 ) + { + bool audio_paused = (audio_status() & AUDIO_STATUS_PAUSE)?true:false; + + /* did someone else (i.e power thread) change audio pause mode? */ + if (wps_state.paused != audio_paused) { + wps_state.paused = audio_paused; + + /* if another thread paused audio, we are probably in car mode, + about to shut down. lets save the settings. */ + if (wps_state.paused) { + settings_save(); +#if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF) + call_storage_idle_notifys(true); +#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 */ + bool pm=false; + FOR_NB_SCREENS(i) + { + if(gui_wps[i].data->peak_meter_enabled) + pm = true; + } + + if (pm) { + 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 = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_NOBLOCK); + if (button != ACTION_NONE) { + break; + } + peak_meter_peek(); + sleep(0); /* Sleep until end of current tick. */ + + if (TIME_AFTER(current_tick, next_refresh)) { + FOR_NB_SCREENS(i) + { + if(gui_wps[i].data->peak_meter_enabled) + skin_update(&gui_wps[i], WPS_REFRESH_PEAK_METER); + next_refresh += HZ / PEAK_METER_FPS; + } + } + } + + } + + /* The peak meter is disabled + -> no additional screen updates needed */ + else +#endif + { + button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK, + restore ? HZ/100 : HZ/5); + } + + /* Exit if audio has stopped playing. This happens e.g. at end of + playlist or if using the sleep timer. */ + if (!(audio_status() & AUDIO_STATUS_PLAY)) + exit = true; +#ifdef HAVE_TOUCHSCREEN + if (button == ACTION_TOUCHSCREEN) + button = wps_get_touchaction(gui_wps[SCREEN_MAIN].data); +#endif +/* The iPods/X5/M5 use a single button for the A-B mode markers, + defined as ACTION_WPSAB_SINGLE in their config files. */ +#ifdef ACTION_WPSAB_SINGLE + if (!global_settings.party_mode && ab_repeat_mode_enabled()) + { + static int wps_ab_state = 0; + if (button == ACTION_WPSAB_SINGLE) + { + switch (wps_ab_state) + { + case 0: /* set the A spot */ + button = ACTION_WPS_ABSETA_PREVDIR; + break; + case 1: /* set the B spot */ + button = ACTION_WPS_ABSETB_NEXTDIR; + break; + case 2: + button = ACTION_WPS_ABRESET; + break; + } + wps_ab_state = (wps_ab_state+1) % 3; + } + } +#endif + switch(button) + { + case ACTION_WPS_CONTEXT: + { + gwps_leave_wps(); + /* if music is stopped in the context menu we want to exit the wps */ + if (onplay(wps_state.id3->path, + FILE_ATTR_AUDIO, CONTEXT_WPS) == ONPLAY_MAINMENU + || !audio_status()) + return GO_TO_ROOT; + restore = true; + } + break; + + case ACTION_WPS_BROWSE: +#ifdef HAVE_LCD_CHARCELLS + status_set_record(false); + status_set_audio(false); +#endif + gwps_leave_wps(); + return GO_TO_PREVIOUS_BROWSER; + break; + + /* play/pause */ + case ACTION_WPS_PLAY: + if (global_settings.party_mode) + break; + if ( wps_state.paused ) + { + wps_state.paused = false; + if ( global_settings.fade_on_stop ) + fade(true, true); + else + audio_resume(); + } + else + { + wps_state.paused = true; + if ( global_settings.fade_on_stop ) + fade(false, true); + else + audio_pause(); + settings_save(); +#if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF) + call_storage_idle_notifys(true); /* make sure resume info is saved */ +#endif + } + break; + + case ACTION_WPS_VOLUP: + { + FOR_NB_SCREENS(i) + gui_wps[i].data->button_time_volume = current_tick; + global_settings.volume++; + bool res = false; + setvol(); + FOR_NB_SCREENS(i) + { + if(update_onvol_change(&gui_wps[i])) + res = true; + } + if (res) { + restore = true; + restoretimer = RESTORE_WPS_NEXT_SECOND; + } + } + break; + case ACTION_WPS_VOLDOWN: + { + FOR_NB_SCREENS(i) + gui_wps[i].data->button_time_volume = current_tick; + global_settings.volume--; + setvol(); + bool res = false; + FOR_NB_SCREENS(i) + { + if(update_onvol_change(&gui_wps[i])) + res = true; + } + if (res) { + restore = true; + restoretimer = RESTORE_WPS_NEXT_SECOND; + } + } + break; + /* fast forward + OR next dir if this is straight after ACTION_WPS_SKIPNEXT */ + case ACTION_WPS_SEEKFWD: + if (global_settings.party_mode) + break; + if (current_tick -last_right < HZ) + { + if (wps_state.id3->cuesheet) + { + audio_next(); + } + else + { + change_dir(1); + } + } + else + ffwd_rew(ACTION_WPS_SEEKFWD); + last_right = last_left = 0; + break; + /* fast rewind + OR prev dir if this is straight after ACTION_WPS_SKIPPREV,*/ + case ACTION_WPS_SEEKBACK: + if (global_settings.party_mode) + break; + if (current_tick -last_left < HZ) + { + if (wps_state.id3->cuesheet) + { + if (!wps_state.paused) +#if (CONFIG_CODEC == SWCODEC) + audio_pre_ff_rewind(); +#else + audio_pause(); +#endif + audio_ff_rewind(0); + } + else + { + change_dir(-1); + } + } + else + ffwd_rew(ACTION_WPS_SEEKBACK); + last_left = last_right = 0; + break; + + /* prev / restart */ + case ACTION_WPS_SKIPPREV: + if (global_settings.party_mode) + break; + last_left = current_tick; +#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() ) + { + if ( ab_after_A_marker(wps_state.id3->elapsed) ) + { + ab_jump_to_A_marker(); + break; +#if (AB_REPEAT_ENABLE == 2) + } else { + ab_reset_markers(); +#endif + } + } + else + /* ...otherwise, do it normally */ +#endif + play_hop(-1); + break; + + /* next + OR if skip length set, hop by predetermined amount. */ + case ACTION_WPS_SKIPNEXT: + if (global_settings.party_mode) + break; + last_right = current_tick; +#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() ) + { + if ( ab_before_A_marker(wps_state.id3->elapsed) ) + { + ab_jump_to_A_marker(); + break; +#if (AB_REPEAT_ENABLE == 2) + } else { + ab_reset_markers(); +#endif + } + } + else + /* ...otherwise, do it normally */ +#endif + play_hop(1); + break; + /* next / prev directories */ + /* and set A-B markers if in a-b mode */ + case ACTION_WPS_ABSETB_NEXTDIR: + if (global_settings.party_mode) + break; +#if defined(AB_REPEAT_ENABLE) + if (ab_repeat_mode_enabled()) + { + ab_set_B_marker(wps_state.id3->elapsed); + ab_jump_to_A_marker(); + } + else +#endif + { + change_dir(1); + } + break; + case ACTION_WPS_ABSETA_PREVDIR: + if (global_settings.party_mode) + break; +#if defined(AB_REPEAT_ENABLE) + if (ab_repeat_mode_enabled()) + ab_set_A_marker(wps_state.id3->elapsed); + else +#endif + { + change_dir(-1); + } + break; + /* menu key functions */ + case ACTION_WPS_MENU: + gwps_leave_wps(); + return GO_TO_ROOT; + break; + + +#ifdef HAVE_QUICKSCREEN + case ACTION_WPS_QUICKSCREEN: + { + gwps_leave_wps(); + if (quick_screen_quick(button)) + return SYS_USB_CONNECTED; + restore = true; + } + break; +#endif /* HAVE_QUICKSCREEN */ + + /* screen settings */ +#ifdef BUTTON_F3 + case ACTION_F3: + { + gwps_leave_wps(); + if (quick_screen_f3(BUTTON_F3)) + return SYS_USB_CONNECTED; + restore = true; + } + break; +#endif /* BUTTON_F3 */ + + /* pitch screen */ +#ifdef HAVE_PITCHSCREEN + case ACTION_WPS_PITCHSCREEN: + { + gwps_leave_wps(); + if (1 == gui_syncpitchscreen_run()) + return SYS_USB_CONNECTED; + restore = true; + } + break; +#endif /* HAVE_PITCHSCREEN */ + +#ifdef AB_REPEAT_ENABLE + /* reset A&B markers */ + case ACTION_WPS_ABRESET: + if (ab_repeat_mode_enabled()) + { + ab_reset_markers(); + update = true; + } + break; +#endif /* AB_REPEAT_ENABLE */ + + /* stop and exit wps */ + case ACTION_WPS_STOP: + if (global_settings.party_mode) + break; + bookmark = true; + exit = true; + break; + + case ACTION_WPS_ID3SCREEN: + { + gwps_leave_wps(); + browse_id3(); + restore = true; + } + break; +#ifdef HAVE_TOUCHSCREEN + case ACTION_TOUCH_SHUFFLE: /* toggle shuffle mode */ + { + global_settings.playlist_shuffle = + !global_settings.playlist_shuffle; +#if CONFIG_CODEC == SWCODEC + dsp_set_replaygain(); +#endif + if (global_settings.playlist_shuffle) + playlist_randomise(NULL, current_tick, true); + else + playlist_sort(NULL, true); + } + break; + case ACTION_TOUCH_REPMODE: /* cycle the repeat mode setting */ + { + const struct settings_list *rep_setting = + find_setting(&global_settings.repeat_mode, NULL); + option_select_next_val(rep_setting, false, true); + audio_flush_and_reload_tracks(); + } + break; +#endif /* HAVE_TOUCHSCREEN */ + /* this case is used by the softlock feature + * it requests a full update here */ + case ACTION_REDRAW: + wps_state.do_full_update = true; + break; + case ACTION_NONE: /* Timeout, do a partial update */ + update = true; + ffwd_rew(button); /* hopefully fix the ffw/rwd bug */ + break; +#ifdef HAVE_RECORDING + case ACTION_WPS_REC: + exit = true; + break; +#endif + case SYS_POWEROFF: + default_event_handler(SYS_POWEROFF); + break; + case ACTION_WPS_VIEW_PLAYLIST: + gwps_leave_wps(); + if (playlist_viewer()) /* true if USB connected */ + return SYS_USB_CONNECTED; + restore = true; + break; + default: + if(default_event_handler(button) == SYS_USB_CONNECTED) + return GO_TO_ROOT; + update = true; + break; + } + + if (wps_state.do_full_update || update) + { +#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD) + gwps_caption_backlight(&wps_state); +#endif + FOR_NB_SCREENS(i) + { +#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) + if (lcd_active() +#ifdef HAVE_REMOTE_LCD + /* currently, all remotes are readable without backlight + * so still update those */ + || (i == SCREEN_REMOTE) +#endif + ) +#endif + { + skin_update(&gui_wps[i], WPS_REFRESH_NON_STATIC); + } + } + wps_state.do_full_update = false; + update = false; + } + + if (restore && wps_state.id3 && + ((restoretimer == RESTORE_WPS_INSTANTLY) || + TIME_AFTER(current_tick, restoretimer))) + { + restore = false; + restoretimer = RESTORE_WPS_INSTANTLY; + gwps_fix_statusbars(); +#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) + lcd_activation_set_hook(wps_lcd_activation_hook); +#endif + FOR_NB_SCREENS(i) + { + screens[i].stop_scroll(); + gui_wps_display(&gui_wps[i]); + } + } + + if (exit) { +#ifdef HAVE_LCD_CHARCELLS + status_set_record(false); + status_set_audio(false); +#endif + if (global_settings.fade_on_stop) + fade(false, true); + + if (bookmark) + bookmark_autobookmark(); + audio_stop(); +#ifdef AB_REPEAT_ENABLE + ab_reset_markers(); +#endif + gwps_leave_wps(); +#ifdef HAVE_RECORDING + if (button == ACTION_WPS_REC) + return GO_TO_RECSCREEN; +#endif + if (global_settings.browse_current) + return GO_TO_PREVIOUS_BROWSER; + return GO_TO_PREVIOUS; + } + + if (button && !IS_SYSEVENT(button) ) + storage_spin(); + } + return GO_TO_ROOT; /* unreachable - just to reduce compiler warnings */ +} + +/* this is called from the playback thread so NO DRAWING! */ +static void track_changed_callback(void *param) +{ + wps_state.id3 = (struct mp3entry*)param; + wps_state.nid3 = audio_next_track(); + if (wps_state.id3->cuesheet) + { + cue_find_current_track(wps_state.id3->cuesheet, wps_state.id3->elapsed); + cue_spoof_id3(wps_state.id3->cuesheet, wps_state.id3); + } + wps_state.do_full_update = true; +} +static void nextid3available_callback(void* param) +{ + (void)param; + wps_state.nid3 = audio_next_track(); + wps_state.do_full_update = true; +} + + +static void wps_state_init(void) +{ + wps_state.ff_rewind = false; + wps_state.paused = false; + if(audio_status() & AUDIO_STATUS_PLAY) + { + wps_state.id3 = audio_current_track(); + wps_state.nid3 = audio_next_track(); + } + else + { + wps_state.id3 = NULL; + wps_state.nid3 = NULL; + } + /* We'll be updating due to restore initialized with true */ + wps_state.do_full_update = false; + /* add the WPS track event callbacks */ + add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, track_changed_callback); + add_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, false, nextid3available_callback); +} + + +#ifdef HAVE_LCD_BITMAP +static void statusbar_toggle_handler(void *data) +{ + (void)data; + int i; + gwps_fix_statusbars(); + + FOR_NB_SCREENS(i) + { + struct viewport *vp = &gui_wps[i].data->viewports[0].vp; + bool draw = wpsbars & (VP_SB_ONSCREEN(i) | VP_SB_IGNORE_SETTING(i)); + if (!draw) + { + vp->y = 0; + vp->height = screens[i].lcdheight; + } + else + { + bool bar_at_top = statusbar_position(i) != STATUSBAR_BOTTOM; + vp->y = bar_at_top?STATUSBAR_HEIGHT:0; + vp->height = screens[i].lcdheight - STATUSBAR_HEIGHT; + } + } +} +#endif + +void gui_sync_wps_init(void) +{ + int i; + FOR_NB_SCREENS(i) + { + wps_data_init(&wps_datas[i]); +#ifdef HAVE_ALBUMART + wps_datas[i].wps_uses_albumart = 0; +#endif +#ifdef HAVE_REMOTE_LCD + wps_datas[i].remote_wps = (i != 0); +#endif + gui_wps[i].data = &wps_datas[i]; + gui_wps[i].display = &screens[i]; + /* Currently no seperate wps_state needed/possible + so use the only available ( "global" ) one */ + gui_wps[i].state = &wps_state; + } +#ifdef HAVE_LCD_BITMAP + add_event(GUI_EVENT_STATUSBAR_TOGGLE, false, statusbar_toggle_handler); +#endif +#if LCD_DEPTH > 1 + unload_wps_backdrop(); +#endif +#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 + unload_remote_wps_backdrop(); +#endif +} + +#ifdef HAVE_ALBUMART +/* Returns true if at least one of the gui_wps screens has an album art + tag in its wps structure */ +bool gui_sync_wps_uses_albumart(void) +{ + int i; + FOR_NB_SCREENS(i) { + struct gui_wps *gwps = &gui_wps[i]; + if (gwps->data && (gwps->data->wps_uses_albumart != WPS_ALBUMART_NONE)) + return true; + } + return false; +} +#endif diff --git a/apps/gui/wps.h b/apps/gui/wps.h new file mode 100644 index 0000000000..90614cb65b --- /dev/null +++ b/apps/gui/wps.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: gwps-common.h 20492 2009-03-23 17:19:48Z alle $ + * + * Copyright (C) 2002 Björn Stenberg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef _MUSICSCREEN_H_ +#define _MUSICSCREEN_H_ +#include + +long gui_wps_show(void); + + +void gui_sync_wps_init(void); + +/* fades the volume, e.g. on pause or stop */ +void fade(bool fade_in, bool updatewps); + +bool ffwd_rew(int button); +void display_keylock_text(bool locked); + +bool is_wps_fading(void); +#endif -- cgit v1.2.3