Rockbox Technical Forums
Rockbox Development => Feature Ideas => Topic started by: tdb on December 27, 2016, 07:11:21 AM
-
Hi
I have been using rockbox for many years almost daily and am very pleased with it. Use it mainly for listening to podcasts and audiobooks and appreciate the tons of features and settings (especially the bookmarking features).
I also use the sleeptimer function often as an aid to go to sleep with a podcast or audiobook. I usually need 15 minutes to go to sleep and that works fine most of the times, but it can happen that a podcast or audiobook wakes me up within that time frame due to some louder recorded sound/voice or sudden change of voice / speaker etc.
For that reason there are only a few podcasts I have chosen to 'talk me into sleep' and keep the sound level low but even then, it sometimes happens I wake up again before sleeptimer shuts down the device.
A solution for this 'problem' might be to add a fade out feature for sleeptimer that takes a couple of minutes (preferably something that can be set by the user)
In practice that could mean I would change the length of the sleeptimer from 15 minutes to 20 or 25 minutes and starts very gradually to fade out after 10 minutes and it takes 10-15 minutes to reach volume 0 and shuts down.
That would make the sleeptimer an even more useful tool as a sleeping aid. I am pretty sure lots of folks are already using it as a sleeping aid, but a feature like this takes it to another level imo.
Maybe there is already something like this possible but I couldn't find it. The fade on stop option is too short and not specific for the sleep timer so that doesn't really help.
-
Here is a lua script that should do what you want, feel free to tweak it to your hearts desire
set your sleep timer and start your songs
copy into a file called Slow_fade.lua or something like that and run it from the file browser
choose a timeout and it will reduce the volume over the span you selected
when it hits the minimum volume playback will stop
--Bilgus 12-2016
require "actions"
require "buttons"
TIMEOUT = 0
SOUND_VOLUME = 0
function say_msg(message, timeout)
rb.splash(1, message)
rb.sleep(timeout * rb.HZ)
end
function say_value(value,message,timeout)
local message = string.format(message .. "%d", value)
say_msg(message, timeout)
end
function cfg_num_setting(str_name)
local file = io.open(rb.ROCKBOX_DIR .. "/config.cfg", "r")
if not file then
return nil
end
local value = nil
local contents = file:read("*all")
i, j = string.find(contents, str_name .. ":")
if j ~= nil then
file:seek ("set", rb.atoi(j))
value = file:read ("*num")
end
file:close() -- GC takes care of this if you would've forgotten it
return value
end
function cfg_str_setting(str_name)
local file = io.open(rb.ROCKBOX_DIR .. "/config.cfg", "r")
if not file then
return nil
end
local str = nil
local contents = file:read("*all")
i, j = string.find(contents, str_name .. ":")
if j ~= nil then
file:seek ("set", rb.atoi(j))
str = file:read ("*line")
str = string.gsub(str, "%s", "")
else
str ="!"
end
file:close() -- GC takes care of this if you would've forgotten it
return str
end
function ShowMainMenu() -- we invoke this function every time we want to display the main menu of the script
local s = 0
local mult = 1
local unit = " Minutes"
while s == 0 or s == 5 do -- don't exit of program until user selects Exit
if mult < 1 then
mult = 1
s = 0
end
mainmenu = {"More", mult * 1 .. unit, mult * 5 .. unit, mult * 10 .. unit, mult * 15 .. unit, "Less", "Exit"} -- define the items of the menu
s = rb.do_menu("Reduce volume over", mainmenu, s, false) -- actually tell Rockbox to draw the menu
-- In the line above: "Test" is the title of the menu, mainmenu is an array with the items
-- of the menu, nil is a null value that needs to be there, and the last parameter is
-- whether the theme should be drawn on the menu or not.
-- the variable s will hold the index of the selected item on the menu.
-- the index is zero based. This means that the first item is 0, the second one is 1, etc.
if s == 0 then mult = mult + 1
elseif s == 1 then TIMEOUT = mult
elseif s == 2 then TIMEOUT = mult * 5
elseif s == 3 then TIMEOUT = mult * 10
elseif s == 4 then TIMEOUT = mult * 15
elseif s == 5 then mult = mult - 1 -- User selected to exit
elseif s == 6 then os.exit() -- User selected to exit
elseif s == -2 then os.exit() -- -2 index is returned from do_menu() when user presses the key to exit the menu (on iPods, it's the left key).
-- In this case, user probably wants to exit (or go back to last menu).
else rb.splash(2 * rb.HZ, "Error! Selected index: " .. s) -- something strange happened. The program shows this message when
-- the selected item is not on the index from 0 to 3 (in this case), and displays
-- the selected index. Having this type of error handling is not
-- required, but it might be nice to have Especially while you're still
-- developing the plugin.
end
end
end
ShowMainMenu()
rb.lcd_clear_display()
rb.lcd_update()
--if rb.strcasecmp(cfg_str_setting("keypress restarts sleeptimer"),"on") == 0 then
--say_msg("keypress restarts timer", 1)
--end
local volume = cfg_num_setting("volume")
if volume == nil then
volume = rb.sound_default(SOUND_VOLUME)
end
local vol_min = rb.sound_min(SOUND_VOLUME)
local volsteps = -(vol_min - volume)
local seconds = (TIMEOUT * 60) / volsteps
local sec_left = (TIMEOUT * 60)
local hb = 0
local action = rb.get_action(rb.contexts.CONTEXT_STD, 0)
if rb.pcm_is_playing() then
while ((volume > vol_min) and (action ~= rb.actions.ACTION_STD_CANCEL)) do
rb.lcd_clear_display()
say_value(volume,sec_left .. " Sec, Volume: ", 1)
local i = seconds * 2
while ((i > 0) and (action ~= rb.actions.ACTION_STD_CANCEL)) do
i = i - 1
rb.lcd_drawline(hb, 1, hb, 1)
rb.lcd_update()
if hb >= rb.LCD_WIDTH then
hb = 0
rb.lcd_clear_display()
say_value(volume,sec_left .. " Sec, Volume: ", 1)
end
hb = hb + 1
rb.sleep(rb.HZ / 2)
action = rb.get_action(rb.contexts.CONTEXT_STD, 0)
rb.yield()
end
volume = volume - 1
rb.sound_set(SOUND_VOLUME, volume);
sec_left = sec_left - seconds
end
rb.audio_stop()
rb.lcd_clear_display()
rb.lcd_update()
say_msg("Playback Stopped", 5)
os.exit()
else
rb.lcd_clear_display()
rb.lcd_update()
say_msg("Nothing is playing", 2)
os.exit()
end
-
Awesome, thanks a lot!
Searching the internet yesterday I found a similar solution someone made for itunes with applescript http://www.jonathanlaliberte.com/2010/03/15/itunes-sleep-timer-with-fade-out-applescript/
Tried your script and added the lua script to my Sleeptimer shortcuts. Works like a charm.
[shortcut]
name: Start Sleep Timer
type: time
data: sleep 25
[shortcut]
name: Stop Sleep Timer
type: time
data: sleep 0
[shortcut]
type: browse
data: /Slow_fade.lua
name: Slow Fade
Will experiment with the script and probably try to optimize the 'fade out curve' a bit to make it work best for me. I think I understand the most important parts of the script so it is very nice to be able to adjust it myself :)
-
No Problem, Once you get something awesome post it here for the next guy :D
-
Hi Bilgus - I have tried the scripts on 3 of my Sansa Clip+ players and work fine but my old Sansa Clip hangs immediately after I start the script.
Has the first Clip some limitations the Clip+ doesn't have? Unfortunately it is the only Clip I have, so I am not able to check on another Clip. Not a big problem. I will also test the script on my e200's and C200 later (I got a bunch of Sansa players :) )
Updated them all to the latest dev build (b772782).
BTW - I am not planning on doing anything fancy with your script. Tested it today a couple of times and I think the linear volume decrease is just working as gradually as I was hoping for. Maybe only change the default setting from 1 - 5 - 10 - 15 minutes to 10 - 15 - 20.
-
I'll have to look into it might be that there isn't enough buffer free on the clip, also you can add a save file and then have your last setting as the first one that comes up
-
I don't even see the lua viewer on the clip you must have a V1 clip I see in the
defines that one only has a plugin buffer of 10000 where as most of the rest have one at 80000 or >
I'll make a real live fade to sleep plugin in the next few days
-
I don't even see the lua viewer on the clip you must have a V1 clip I see in the
defines that one only has a plugin buffer of 10000 where as most of the rest have one at 80000 or >
I'll make a real live fade to sleep plugin in the next few days
The Clip is a v2 so the buffer for that one might be too low as well?
Anyway, having the feature as a plugin would indeed be nice and might encourage more people to try it out.
Lua script is working fine though, was sleeping like a baby last night ;)
-
Tested the Lua script on my Pandora, no issues. Not working on my C200v2 though. Message 'can't open....'. I guess Lua isn't available for the C200 as well.
Will try it out on my E200 later.
-
Lua script works fine on my E200 as well.
The script seems to use the volume to start the fade timer with from the config.cfg file - is it possible with Lua to 'read' the actual playing volume?
-
not that I have figured out as of yet -- Not saying there isn't just saying out of 5 or 6 ways I thought might work the cfg file worked the others not so much.
-
OK - np
I am going to change it to a fixed volume level then that is always comfortable to start with.
-
I've added the requisite functions to rb plugins with a commit on gerrit it will allow me to pull in the current volume level and the sleep timer seconds I'll post an updated script if and when it gets committed
-
Nice - that will improve an already very helpful script :)
I have been using the script almost daily for more than a month now and it is working brilliantly. After falling asleep I didn't wake up during duration of the sleeptimer once. So it is doing in practice exactly what I was hoping for.
Would this be possible to script after the update: being able to reset the fade out- and sleep-timer while the script is running with a keypress (or two to prevent a key being accidentally pressed)? Especially useful when you notice you have problems getting to sleep and the previously set sleeptimer/fade out duration is not long enough.
-
I've added the requisite functions to rb plugins with a commit on gerrit it will allow me to pull in the current volume level and the sleep timer seconds I'll post an updated script if and when it gets committed
Sorry for grave digging this thread.
Noticed there are indeed some commits on gerrit, but they don't seem to be implemented yet. Still a chance we will see the feature in the dev builds?
-
I've got a plugin that implements it in progress but I still haven't finished it yet but I will eventually get interested in it again
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2017 William Wilgus
*
* 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 "plugin.h"
#include "lib/pluginlib_actions.h"
#define settings_filename PLUGIN_APPS_DIR "/fade2sleep_settings.f2s"
#define MSTR_CT(x) (sizeof(x)/sizeof(0[x]))
#define MFMT_PLAYLIST "%s %s %s;"
#define MFMT_TIME "%s %s;"
#define MFMT_VOL "%s %d %s;"
#define MFMT_STR "%s;"
#define MFMT_SEPARATOR " ;"
#define MENU_SETTINGS_BEGIN "",
#define MENUSETTING_VOLUME(item) setting_strings[item],settings->table[item],\
rb->sound_unit(SOUND_VOLUME)
#define MENUSETTING_TRIGGER(item) setting_strings[item],\
trigger_strings[settings->table[item]]
#define MENUSETTING_ACTION(item) action_strings[settings->table[item]]
#define MENUSETTING_PLAY(item) setting_strings[item],\
play_strings[settings->table[item]],\
(settings->table[item])? settings->play : "\0"
#define WATCHED_EV(flag) ((watched_events & flag) == flag) ? flag : 0
#define THREAD_STACK_SIZE DEFAULT_STACK_SIZE
/* use long for aligning */
static unsigned long thread_stack[THREAD_STACK_SIZE/sizeof(long)];
#define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 1)
#define TIMEOUT_VOL_CHANGE HZ/2
static unsigned int thread_id;
static struct event_queue thread_q SHAREDBSS_ATTR;
const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
//#define MAX_MENU_ENTRIES 10 /* menu string pointers statically allocated */
//const char* menu_strings[MAX_MENU_ENTRIES];// * sizeof(const char*)
static unsigned int triggered_events = 0;
static unsigned int watched_events = 0;
static bool quit = false, in_usb_mode = false;
/*Time setting formatting (NOT RTC)*/
#define MS_IN_TICK (1000U/HZ)
enum e_fmt_time_auto_idx
{
UNIT_IDX_HR = 0,
UNIT_IDX_MIN,
UNIT_IDX_SEC,
UNIT_IDX_MS,
UNIT_IDX_TIME_COUNT,
};
/* format_time_auto */
#define UNIT_IDX_MASK 0x01FFU /*Return only Unit_IDX*/
#define UNIT_TRIM_ZERO 0x0200U /*Don't show leading zero on max_idx*/
#define UNIT_LOCK_HR 0x0400U /*Don't Auto Range below this field*/
#define UNIT_LOCK_MIN 0x0800U /*Don't Auto Range below this field*/
#define UNIT_LOCK_SEC 0x1000U /*Don't Auto Range below this field*/
static const char *unit_strings[] =
{
[UNIT_INT] = "", [UNIT_MS] = "ms",
[UNIT_SEC] = "s", [UNIT_MIN] = "min",
[UNIT_HOUR]= "hr", [UNIT_KHZ] = "kHz",
[UNIT_DB] = "dB", [UNIT_PERCENT] = "%",
[UNIT_MAH] = "mAh", [UNIT_PIXEL] = "px",
[UNIT_PER_SEC] = "per sec",
[UNIT_HERTZ] = "Hz",
[UNIT_MB] = "MB", [UNIT_KBIT] = "kb/s",
[UNIT_PM_TICK] = "units/10ms",
};
enum e_self_triggered_events
{ /* TRIGGERED WITHIN THIS PLUGIN */
SELF_EVENT_CHECK_TRIGGERED = 0,
SELF_EVENT_TIMEOUT_REACHED = 1,
SELF_EVENT_FINALVOL_REACHED = 2,
};
enum e_triggered_events
{
//TRIG_EVENT_=0x0001,
TRIG_EVENT_START_PLAYBACK = 0x0002,
TRIG_EVENT_TRACK_SKIP = 0x0004,
TRIG_EVENT_TRACK_CHANGE = 0x0008,
TRIG_EVENT_TRACK_FINISH = 0x0010,
TRIG_EVENT_VOL_CHANGED = 0x0020,
TRIG_EVENT_SCREEN_CHANGED = 0x0040,
TRIG_EVENT_SCREEN_ON = 0x0080,
TRIG_EVENT_VOL_FINAL = 0x0100,
TRIG_EVENT_PAUSE = 0x0200,
TRIG_EVENT_RESUME = 0x0400,
TRIG_EVENT_PLAY = 0x0800,
TRIG_EVENT_STOP = 0x1000,
TRIG_EVENT_TIMEOUT = 0x2000,
//TRIG_EVENT_=0x4000,
//TRIG_EVENT_=0x8000,
};
enum e_play_str
{
PLAY_USECURRENT = 0,
PLAY_LOAD,
};
static const char * play_strings[] =
{
[PLAY_USECURRENT]"Use Current",
[PLAY_LOAD] "Load",
};
enum e_save_str
{
SAV_SAVE = 0,
SAV_SAVED,
};
static const char * saved_strings[] =
{
[SAV_SAVE] "Save",
[SAV_SAVED]"Saved",
};
enum e_trigger_str
{
TRIG_NONE = 0,
TRIG_TIMEOUT,
TRIG_FINALVOL,
TRIG_PAUSE,
TRIG_VOLCHANGE,
TRIG_TRACKCHANGE,
TRIG_ANY,
};
static const char * trigger_strings[] =
{
[TRIG_NONE] "None",
[TRIG_TIMEOUT] "Timeout",
[TRIG_FINALVOL] "Final Volume",
[TRIG_PAUSE] "Pause",
[TRIG_VOLCHANGE] "Volume Change",
[TRIG_TRACKCHANGE]"Track Change",
[TRIG_ANY] "Any Action",
};
enum e_delay_str
{
DLY_NONE = 0,
DLY_TIMEOUT,
DLY_TRACKCHANGE,
DLY_PAUSERESUME,
DLY_PLAYSTOP,
};
static const char * delay_strings[] =
{
[DLY_NONE] "None",
[DLY_TIMEOUT] "Timeout",
[DLY_TRACKCHANGE]"Track Change",
[DLY_PAUSERESUME]"Pause/Resume",
[DLY_PLAYSTOP] "Play/Stop",
};
enum e_curvol_str
{
CURVOL_NONE = 0,
CURVOL_START,
CURVOL_END,
};
static const char * cur_vol_strings[] =
{
[CURVOL_NONE] "None",
[CURVOL_START]"Start",
[CURVOL_END] "End",
};
enum e_action_str
{
ACT_NONE = 0,
ACT_SLEEP,
ACT_STARTOVER,
ACT_INVERTFADE,
ACT_EXIT,
};
static const char * action_strings[] =
{
[ACT_NONE] "None",
[ACT_SLEEP] "Sleep",
[ACT_STARTOVER] "Start Over",
[ACT_INVERTFADE]"Invert Fade",
[ACT_EXIT] "Exit",
};
enum e_ny_str
{
NY_NO = 0,
NY_YES,
};
static const char * ny_strings[] =
{
[NY_NO] "No",
[NY_YES]"Yes",
};
enum enum_menu
{ /*same order as they will be displayed in the menu*/
M_PLAY = 0,
M_DELAY,
M_ACTDELAY,
M_USECURVOL,
M_STARTVOL,
M_ENDVOL,
M_TIMEOUT,
M_TRIG1,
M_ACT1,
M_TRIG2,
M_ACT2,
M_TRIG3,
M_ACT3,
M_TSR,
M_SEP,
M_SAVE,
M_RUN,
M_EXIT,
M_ITEM_COUNT,
};
static const char * setting_strings[M_ITEM_COUNT] =
{//[]"",
[M_PLAY] "Play",
[M_DELAY] "Delay",
[M_ACTDELAY] "After",
[M_USECURVOL] "Use Cur Volume",
[M_STARTVOL] "Fade from :",
[M_ENDVOL] "to :",
[M_TIMEOUT] "Timeout",
[M_TRIG1] "After",
[M_ACT1] "Action1",
[M_TRIG2] "After",
[M_ACT2] "Action2",
[M_TRIG3] "After",
[M_ACT3] "Action3",
[M_TSR] "In Background",
/*[M_SEP]*/
[M_SAVE] "Save",
[M_RUN] "Run",
[M_EXIT] "Exit",
};
static const int setting_icons[M_ITEM_COUNT] =
{//Icon_NOICON
[M_DELAY]Icon_Menu_setting,
[M_ACTDELAY]Icon_Playback_menu,
[M_USECURVOL]Icon_Questionmark,
[M_STARTVOL]Icon_EQ,
[M_ENDVOL]Icon_Audio,
[M_TIMEOUT]Icon_Config,
[M_TRIG1]Icon_Menu_setting,
[M_ACT1]Icon_Menu_functioncall,
[M_TRIG2]Icon_NOICON,
[M_ACT2]Icon_NOICON,
[M_TRIG3]Icon_NOICON,
[M_ACT3]Icon_NOICON,
[M_SEP]Icon_NOICON,
[M_SAVE]Icon_Submenu,
[M_EXIT]Icon_Reverse_Cursor,
};
//static int setting_table[M_ITEM_COUNT]={0};
static struct viewport viewport[NB_SCREENS];
static struct fade_settings
{
int vol_orig;
int vol_min;
int vol_max;
int vol_step;
int vol_cur;
bool fade_out;
long timeout_ticks;
long ticks_remaining;
int audio_status;
int duration;
int delay_duration;
int vol_init;
bool init_use_cur;
int vol_final;
bool final_use_cur;
int actions;
int sleep;
bool tsr;
} fade_set;
static struct settings
{
uint32_t crc;
char play[MAX_PATH];
int table[M_ITEM_COUNT];
} settings;
static const char * dyn_menu_get_entry(int selected_item, void * data,
char * buffer, size_t buffer_len)
{
//(void)data;
(void)buffer;
(void)buffer_len;
char * menustr = ((char**) data)[selected_item];
return menustr;
}
static enum themable_icons menu_get_icon(int selected_item, void * data)
{
(void)data;
if ((unsigned) selected_item >= M_ITEM_COUNT)
return Icon_NOICON;
return setting_icons[selected_item];//data[selected_item];// : Icon_NOICON;
}
static int do_dyn_menu(const char* title,
char* strentry, const char ** entrylist, int entries,
int *cur_selection, int *list_start,
list_get_icon *icon_callback,
struct viewport parent[NB_SCREENS], bool hide_theme)
{
/*
Creates a menu, can use non constant strings by supplying a comma
separated list of menu items via strentry ex. "item1;item2;item3;item4"
strentry may be a buffer filled by any means (sprintf) or a string literal
but NOT const since every separator ';' will be replaced by a '\0'
ALTERNATIVELY can use a list of const strings via entrylist
ex. static const char * menu_str[] ={"item1", "item2", "item3"};
do_dyn_menu(titlestr, menustr, entrylist, entries, start, hidetheme)
titlestr - title for the menu
menustr - comma separated list of menu entries
entrylist - ALTERNATIVE to menustr pointer to const string list
entries - number of entries in menustr if > MAX_MENU_ENTRIES will be truncated.
cur_sel - sets menu item selected, returns users selection
list_start - sets menu entry at top of list, keeps position between calls
hide_theme - hides theme elements such as the status bar and background
returns selected item index, -2 if canceled, <0 on other error
*/
enum{MAX_DYN_MENU_ENTRIES=M_ITEM_COUNT +1 };
static const char* menu_strings[MAX_DYN_MENU_ENTRIES];
char *remain, *token = NULL;
const char * const sep= ";";
struct gui_synclist list;
int result = 0;
int action;
entries = MIN(MAX_DYN_MENU_ENTRIES, entries);
if(menu_strings != NULL && entrylist == NULL)
{
for(int i=0; i < entries; i++)
{
token = rb->strtok_r(strentry, sep, &remain);
strentry = NULL; /*strtok will continue with sequence*/
menu_strings[i] = (token != NULL)? token : "???";
}
entrylist = menu_strings;
}
rb->gui_synclist_init(&list, dyn_menu_get_entry, entrylist, true, 1, parent);
rb->gui_synclist_set_title(&list, (unsigned char*) title, Icon_Rockbox);
if (icon_callback)
rb->gui_synclist_set_icon_callback(&list, *icon_callback);
rb->gui_synclist_set_nb_items(&list, entries);
rb->gui_synclist_select_item(&list, (cur_selection) ? *cur_selection : 0);
FOR_NB_SCREENS(i)
{
if (list_start)
list.start_item[i] = *list_start;
rb->viewportmanager_theme_enable(i, !hide_theme, parent);
}
rb->gui_synclist_draw(&list);
while (result == 0)
{
action = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
if (rb->gui_synclist_do_button(&list, &action, LIST_WRAP_UNLESS_HELD))
continue;
switch (action)
{
case ACTION_STD_OK:
{
result = 1;
break;
}
case ACTION_STD_CANCEL:
{
result = -1;
break;
}
}
}
if (cur_selection)
*cur_selection = rb->gui_synclist_get_sel_pos(&list);
FOR_NB_SCREENS(i)
{
rb->viewportmanager_theme_undo(i, false);
if (list_start)
{
if (i == 0)
*list_start = 0;
*list_start |= list.start_item[i];
}
}
return result;
}
/*
unsigned int ms_to_ticks(unsigned int ms)
{
return ms/MS_IN_TICK;
}
unsigned int ticks_to_ms(unsigned int ticks)
{
return ticks * MS_IN_TICK;
}
*/
/* time_split_units()
split time values depending on base unit
unit_idx: UNIT_HOUR, UNIT_MIN, UNIT_SEC, UNIT_MS
abs_value: absolute time value
units_in: array of unsigned ints with UNIT_IDX_TIME_COUNT fields
*/
static unsigned int time_split_units(int unit_idx, unsigned long abs_val,
unsigned int (*units_in)[UNIT_IDX_TIME_COUNT])
{
unsigned int base_idx = UNIT_IDX_HR;
int hours;
int minutes = 0;
int seconds = 0;
int millisec = 0;
switch (unit_idx & UNIT_IDX_MASK) /*Mask off upper bits*/
{
case UNIT_MS:
base_idx = UNIT_IDX_MS;
millisec = abs_val;
abs_val = abs_val / 1000U;
millisec = millisec - (1000U * abs_val);
/* fallthrough and calculate the rest of the units */
case UNIT_SEC:
if (base_idx == UNIT_IDX_HR)
base_idx = UNIT_IDX_SEC;
seconds = abs_val;
abs_val = abs_val / 60U;
seconds = seconds - (60U * abs_val);
/* fallthrough and calculate the rest of the units */
case UNIT_MIN:
if (base_idx == UNIT_IDX_HR)
base_idx = UNIT_IDX_MIN;
minutes = abs_val;
abs_val = abs_val / 60U;
minutes = minutes -(60U * abs_val);
/* fallthrough and calculate the rest of the units */
case UNIT_HOUR:
default:
hours = abs_val;
break;
}
(*units_in)[UNIT_IDX_HR] = hours;
(*units_in)[UNIT_IDX_MIN] = minutes;
(*units_in)[UNIT_IDX_SEC] = seconds;
(*units_in)[UNIT_IDX_MS] = millisec;
return base_idx;
}
/* format_time_auto - return an auto ranged time string;
buffer: needs to be at least 64 characters
unit_idx: specifies lowest or base index of the value
add | UNIT_LOCK_ to prevent autorange below this index
add | UNIT_TRIM_ZERO to supress leading zero on the largest unit
value: should be passed in the same form as unit_idx
supress_unit: if true unit string is NOT printed
idx_pos[2]: (if !NULL) [0] specifies an index of interest,
the offset and width for that index will be returned.
In field [0] offset, field [1] length
Ex: given 12:34:56.78 if you pass the idx_pos UNIT_IDX_MIN
idx_pos returns -> {3,2}.. offset(3) and length(2) = '34'
*/
static const char *format_time_auto(char *buffer, int buf_len, const long value,
int unit_idx, bool supress_unit,
unsigned char (*idx_pos)[2])
{
const char * const sign = &"-"[value < 0 ? 0 : 1];
bool is_rtl = rb->lang_is_rtl();
unsigned int timebuf_len = 30; /* -2147483648:00:00.00\0 */
char *timebuf;/*timebuf[24]; shared with buffer instead*/
int len, left_offset;
unsigned char base_idx, max_idx;
unsigned int units_in[UNIT_IDX_TIME_COUNT];
char fwidth[UNIT_IDX_TIME_COUNT] =
{
[UNIT_IDX_HR] = 0, /* hr is variable length */
[UNIT_IDX_MIN] = 2,
[UNIT_IDX_SEC] = 2,
[UNIT_IDX_MS] = 3,
}; /* {0,2,2,3}; Field Widths*/
unsigned char offsets[UNIT_IDX_TIME_COUNT] =
{
[UNIT_IDX_HR] = 10,/* ?:59:59.999 Std offsets */
[UNIT_IDX_MIN] = 7, /*0?:+1:+4.+7 need calculated */
[UNIT_IDX_SEC] = 4,/* 999.59:59:0 RTL offsets */
[UNIT_IDX_MS] = 0,/* 0 .4 :7 :10 won't change */
}; /* {10,7,4,0}; Offsets*/
static const int unitlock[UNIT_IDX_TIME_COUNT] =
{
[UNIT_IDX_HR] = UNIT_LOCK_HR,
[UNIT_IDX_MIN] = UNIT_LOCK_MIN,
[UNIT_IDX_SEC] = UNIT_LOCK_SEC,
[UNIT_IDX_MS] = 0,
}; /* unitlock*/
static const int units[UNIT_IDX_TIME_COUNT] =
{
[UNIT_IDX_HR] = UNIT_HOUR,
[UNIT_IDX_MIN] = UNIT_MIN,
[UNIT_IDX_SEC] = UNIT_SEC,
[UNIT_IDX_MS] = UNIT_MS,
}; /* units*/
buf_len = buf_len - (timebuf_len + 2);
timebuf = &buffer[buf_len + 1]; /* use part of the supplied buffer */
if (buf_len < 32)
return buffer;
if (idx_pos != NULL)
{
(*idx_pos)[0] = MIN((*idx_pos)[0], UNIT_IDX_TIME_COUNT - 1);
unit_idx |= unitlock[(*idx_pos)[0]];
}
base_idx = time_split_units(unit_idx, abs(value), &units_in);
if (units_in[UNIT_IDX_HR] || (unit_idx & unitlock[UNIT_IDX_HR]))
max_idx = UNIT_IDX_HR;
else if (units_in[UNIT_IDX_MIN] || (unit_idx & unitlock[UNIT_IDX_MIN]))
max_idx = UNIT_IDX_MIN;
else if (units_in[UNIT_IDX_SEC] || (unit_idx & unitlock[UNIT_IDX_SEC]))
max_idx = UNIT_IDX_SEC;
else if (units_in[UNIT_IDX_MS])
max_idx = UNIT_IDX_MS;
else /* value is 0*/
max_idx = base_idx;
if (!is_rtl)
{
len = snprintf(timebuf, timebuf_len,
"%02d:%02d:%02d.%03d",
units_in[UNIT_IDX_HR],
units_in[UNIT_IDX_MIN],
units_in[UNIT_IDX_SEC],
units_in[UNIT_IDX_MS]);
fwidth[UNIT_IDX_HR] = len - offsets[UNIT_IDX_HR];
offsets[UNIT_IDX_MS] = fwidth[UNIT_IDX_HR] + offsets[UNIT_IDX_MIN];
offsets[UNIT_IDX_SEC] = fwidth[UNIT_IDX_HR] + offsets[UNIT_IDX_SEC];
offsets[UNIT_IDX_MIN] = fwidth[UNIT_IDX_HR] + 1;
offsets[UNIT_IDX_HR] = 0;
timebuf[offsets[base_idx] + fwidth[base_idx]] = '\0';
left_offset = -(offsets[max_idx]);
left_offset += rb->strlcpy(buffer, sign, buf_len);
/* trim leading zero on the max_idx */
if ((unit_idx & UNIT_TRIM_ZERO) == UNIT_TRIM_ZERO &&
timebuf[offsets[max_idx]] == '0')
{
offsets[max_idx]++;
}
rb->strlcat(buffer, &timebuf[offsets[max_idx]], buf_len);
if (!supress_unit)
{
rb->strlcat(buffer, " ", buf_len);
rb->strlcat(buffer, unit_strings[units[max_idx]], buf_len);
}
}
else /*RTL Languages*/
{
len = snprintf(timebuf, timebuf_len,
"%03d.%02d:%02d:%02d",
units_in[UNIT_IDX_MS],
units_in[UNIT_IDX_SEC],
units_in[UNIT_IDX_MIN],
units_in[UNIT_IDX_HR]);
fwidth[UNIT_IDX_HR] = len - offsets[UNIT_IDX_HR];
left_offset = -(offsets[base_idx]);
/* trim leading zero on the max_idx */
if ((unit_idx & UNIT_TRIM_ZERO) == UNIT_TRIM_ZERO &&
timebuf[offsets[max_idx]] == '0')
{
timebuf[offsets[max_idx]] = timebuf[offsets[max_idx]+1];
fwidth[max_idx]--;
}
timebuf[offsets[max_idx] + fwidth[max_idx]] = '\0';
if (!supress_unit)
{
rb->strlcpy(buffer, unit_strings[units[max_idx]], buf_len);
left_offset += rb->strlcat(buffer, " ", buf_len);
rb->strlcat(buffer, &timebuf[offsets[base_idx]], buf_len);
}
else
rb->strlcpy(buffer, &timebuf[offsets[base_idx]], buf_len);
rb->strlcat(buffer, sign, buf_len);
}
if (idx_pos != NULL)
{
(*idx_pos)[1]= fwidth[*(idx_pos)[0]];
(*idx_pos)[0]= left_offset + offsets[(*idx_pos)[0]];
}
return buffer;
}
static void draw_horiz_scrollbar(struct screen * display, int top, int height, int cur_pct)
{
rb->gui_scrollbar_draw(display, /* screen */
5, /* x */
top, /* y */
LCD_WIDTH-10, /* width */
height , /* height */
100, /* items */
0, /* min_shown */
cur_pct, /* max_shown */
HORIZONTAL); /* flags */
}
static inline int get_button(void)
{
return pluginlib_getaction(HZ/10, plugin_contexts,
ARRAYLEN(plugin_contexts));
}
static bool can_play(void)
{
int audio_status = rb->audio_status();
if ((!audio_status && rb->global_status->resume_index != -1)
&& (rb->playlist_resume() != -1)) {
return true;
}
else if (audio_status & AUDIO_STATUS_PLAY)
return true;
return false;
}
static void resume_audio(bool from_stop)
{
int audio_status = rb->audio_status();
//rb->playlist_resume()
if (from_stop && !audio_status && rb->global_status->resume_index != -1) {
if (rb->playlist_resume() != -1) {
rb->playlist_resume_track(rb->global_status->resume_index,
rb->global_status->resume_crc32,
rb->global_status->resume_elapsed,
rb->global_status->resume_offset);
}
}
else if (audio_status & AUDIO_STATUS_PLAY)
rb->audio_resume();
}
static void pause_audio(void)
{
if (rb->audio_status() & AUDIO_STATUS_PLAY)
rb->audio_pause();
}
int value_to_pct(int value, int min, int max)
{
int pct = 0;
pct = ((value * 100 - min *100)/(abs(max-min)));
return pct;
}
static int clamp_int(int value, int min, int max)
{
int tmp = 0;
if (min > max)
{ /* Swap max and min if needed */
tmp = max;
max = min;
min = tmp;
}
if (value < min)
value = min;
else if (value > max)
value = max;
return value;
}
static int do_select_clamped(int v1, int v2, bool use_v2, int v_min, int v_max)
{
/*
selects val1 or val2 by bool use_v2
false = val1, true = val2
0 = val1 1 = val2
returns selected clamped to v_min/v_max
*/
int value;
if (use_v2)
value = v2;
else
value = v1;
return clamp_int(value, v_min, v_max);
}
static int clamp_int_rollover(int value, int min, int max)
{
int tmp = 0;
if (min > max)
{ /* Swap max and min if needed */
tmp = max;
max = min;
min = tmp;
}
if (value < min)
value = max;
else if (value > max)
value = min;
return value;
}
static int do_volume_change(int vol_step, int vol_min, int vol_max)
{
/*
Negative numbers lower volume
Positive numbers raise volume
clamps to vol_min/volmax
returns current volume
*/
rb->button_clear_queue(); //clear any pending buttons
int vol = rb->sound_get_current(SOUND_VOLUME);
vol = clamp_int(vol + vol_step, vol_min, vol_max);
rb->sound_set_current(SOUND_VOLUME, vol);
return vol;
}
static void do_set_fade(struct fade_settings * fade_set)
{
int duration = fade_set->duration;
int vol_min = rb->sound_min(SOUND_VOLUME);
int vol_max = rb->sound_max(SOUND_VOLUME);
int vol_orig = rb->sound_get_current(SOUND_VOLUME); /* get current volume */
int vol_cur; /*vol_orig is the global volume not necessarily current volume*/
int vol_init;
int vol_final;
int vol_step;
int vol_range;
bool fade_out;
long ticks_remaining;
long timeout_ticks;
vol_init = do_select_clamped(vol_orig, fade_set->vol_init,
!fade_set->init_use_cur, vol_min, vol_max);
fade_set->vol_init = vol_init;
vol_cur = vol_init;
vol_final = do_select_clamped(vol_orig, fade_set->vol_final,
!fade_set->final_use_cur, vol_min, vol_max);
fade_set->vol_final = vol_final;
if (vol_init > vol_final)
{
fade_out = true;
vol_range = vol_init - vol_final; //abs()
vol_step = -1;
}
else
{
fade_out = false;
vol_range = vol_final - vol_init;//abs()
vol_step = 1;
}
fade_set->fade_out = fade_out;
fade_set->vol_step = vol_step;
ticks_remaining = HZ * duration;
if (ticks_remaining < HZ)
ticks_remaining = HZ;
fade_set->ticks_remaining = ticks_remaining;
timeout_ticks = (ticks_remaining / vol_range);
if (timeout_ticks < HZ/10)
timeout_ticks = HZ/10;
fade_set->timeout_ticks = timeout_ticks;
fade_set->vol_min = vol_min;
fade_set->vol_max = vol_max;
fade_set->vol_cur = vol_cur;
fade_set->vol_orig = vol_orig;
}
static bool do_fade(struct fade_settings * fade_set)
{
int vol_min = fade_set->vol_init;
int vol_max = fade_set->vol_final;
int vol_step = fade_set->vol_step;
int vol_cur = fade_set->vol_cur;
int ret = true;
vol_cur = do_volume_change(vol_step, vol_min, vol_max);
if (fade_set->vol_cur == vol_cur)
{
rb->splashf(HZ, "usr = %d", vol_cur);
}
if (vol_cur == vol_max)
{
ret = false;
rb->splashf(HZ, "v = %d", vol_cur);
}
fade_set->vol_cur = vol_cur;
return ret;
}
static int get_button_dpad(int *value, int *h_pos, int * v_pos, unsigned int multiplier)
{
/* Allow user to use left,right, up, down, and (scroll) buttons to set a value
single button press increments/decrements value by 1 * multiplier
repeat button press accelerates inc/dec by multiplier * 2
if h_pos is not NULL then left decrements h_pos, right increments
if v_pos is not NULL then down decrements v_pos, up increments
h_pos/v_pos being defined blocks actions for respective
single press inc/dec of value
Returns 0 if no button matched,
-1 if canceled,
1 if value selected
*/
int button;
int exit_val = 0;
button = get_button();
switch (button)
{
case PLA_RIGHT:
if (h_pos) { *h_pos += 1; break; }
case PLA_UP:
if (v_pos) { *v_pos += 1; break; }
*value+=1 * multiplier;
break;
case PLA_RIGHT_REPEAT:
case PLA_UP_REPEAT:
*value+=2 * multiplier;
break;
#ifdef HAVE_SCROLLWHEEL
case PLA_SCROLL_FWD:
*value+=1 * multiplier;
break;
case PLA_SCROLL_FWD_REPEAT:
*value+=2 * multiplier;
break;
#endif
case PLA_LEFT:
if (h_pos) { *h_pos -= 1; break; }
case PLA_DOWN:
if (v_pos) { *v_pos -= 1; break; }
*value-=1 * multiplier;
break;
case PLA_LEFT_REPEAT:
case PLA_DOWN_REPEAT:
*value-=2 * multiplier;
break;
#ifdef HAVE_SCROLLWHEEL
case PLA_SCROLL_BACK:
*value-=1 * multiplier;
break;
case PLA_SCROLL_BACK_REPEAT:
*value -=2 * multiplier;
break;
#endif
case PLA_SELECT:
case PLA_SELECT_REPEAT:
exit_val = 1;
break;
case PLA_EXIT:
case PLA_CANCEL:
exit_val = -1;
break;
default:
exit_val = 0;
break;
}
return exit_val;
}
void pop_up_frame(struct screen * display, int x, int y, int width, int height)
{
/*
|___________|
| |
| |
|___________|
*/
//int title_height = display->getcharheight() + 5;
//display->puts_scroll(1, 0, title_buf);
//void (*lcd_fillrect)(int x, int y, int width, int height);
display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
display->fillrect(x, y, width, height);
display->set_drawmode(DRMODE_SOLID);
display->hline(0, width, height/3);
display->hline(1, width-1, height);
display->vline(x, 0, height-1);
display->vline(width-x, 0, height-1);
}
int do_set_value(const char * title, int val, int val_min, int val_max, const char * val_lbl)
{
struct screen * display = rb->screens[SCREEN_MAIN];
int title_height = display->getcharheight() + 5;
char title_buf[48]= "\0";
int initial_val = val;
int ret = 0;
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_enable(i, false, NULL);
//rb->lcd_clear_display();
pop_up_frame(display, 0, 0, LCD_WIDTH-1, title_height * 3);
while(ret == 0)
{
ret = get_button_dpad(&val, NULL, NULL, 1); /* allow user to set value*/
val = clamp_int_rollover(val, val_min, val_max);
draw_horiz_scrollbar(display, title_height * 2, title_height/2,
value_to_pct(val, val_min, val_max));
rb->snprintf(title_buf, sizeof(title_buf), "%s %d %s ",
title, val, val_lbl);
display->puts_scroll(1, 0, title_buf);
display->update();
rb->sleep(HZ/100);
}
if (ret == -1)
{
val = initial_val;
rb->splash(HZ/2, "Canceled");
}
display->clear_display();
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_undo(i, true);
//display->update();
return val;
}
int do_set_time(const char * title, int sec, int sec_min, int sec_max)
{
char time_buf[64]= "\0";
const int time_buf_sz = sizeof(time_buf);
struct screen *display = rb->screens[SCREEN_MAIN];
int title_height = display->getcharheight() + 5;
int is_rtl = rb->lang_is_rtl();
const int initial_sec = sec; /* on cancel original time returned*/
int ret = 0;
enum {FIELD_ST = 0, FIELD_CT = 3};
unsigned char idx_pos[2];
int field_pos = (is_rtl? FIELD_ST : FIELD_CT - 1);
int rel_field; /* If lang is RTL then fields/cols are reversed */
static const unsigned char field_cols[FIELD_CT] =
{UNIT_IDX_HR, UNIT_IDX_MIN, UNIT_IDX_SEC};
static const unsigned int multiplier_cols[FIELD_CT] = {3600, 60, 1};
int offset = 1;
const int lcd_chr_width = (LCD_WIDTH/rb->lcd_getstringsize("W", NULL, NULL));
//rb->lcd_clear_display();
pop_up_frame(display, 0, 0, LCD_WIDTH-1, title_height * 3);
while(ret == 0)
{
field_pos = clamp_int_rollover(field_pos, FIELD_ST, FIELD_CT - 1);
/* if lang is RTL then columns flip */
rel_field = (is_rtl? FIELD_CT - field_pos - 1 : field_pos);
idx_pos[0] = field_cols[rel_field];
ret = get_button_dpad(&sec, &field_pos, NULL, multiplier_cols[rel_field]);
/* allow user to set value*/
sec = clamp_int_rollover(sec, sec_min, sec_max);
rb->lcd_puts_scroll(offset, 2, " ");
format_time_auto(time_buf, time_buf_sz, sec, UNIT_SEC, false, &idx_pos);
offset = (lcd_chr_width - rb->strlen(time_buf))/2;
rb->lcd_puts_scroll(offset, 2, time_buf);
rb->lcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
time_buf[idx_pos[0] + idx_pos[1]] = '\0';
/*draw selected position inverse*/
rb->lcd_puts_scroll(offset + idx_pos[0], 2, &time_buf[idx_pos[0]]);
rb->lcd_set_drawmode(DRMODE_SOLID);
rb->lcd_puts_scroll(1,0,title);
rb->lcd_update();
rb->sleep(HZ/100);
}
if (ret == -1)
{
sec = initial_sec;
rb->splash(HZ/2, "Canceled");
}
rb->lcd_clear_display();
rb->lcd_update();
return sec;
}
uint32_t crc_settings(struct settings * set)
{
//struct settings set_tmp
/* some fields are ignored in crc calculation */
uint32_t current_crc = set->crc;
set->crc = 0;
int vol_start = set->table[M_STARTVOL];
int vol_end = set->table[M_ENDVOL];
if (set->table[M_USECURVOL] == CURVOL_START)
set->table[M_STARTVOL] = 0;
if (set->table[M_USECURVOL] == CURVOL_END)
set->table[M_ENDVOL] = 0;
set->table[M_SAVE] = 0;
uint32_t crc = rb->crc_32(set, sizeof(*set), 0xffffffff);
/* put back ignored fields */
set->crc = current_crc;
set->table[M_STARTVOL] = vol_start;
set->table[M_ENDVOL] = vol_end;
return crc;
}
void load_settings(struct settings * settings, char* filename)
{
int fh = rb->open(filename, O_RDONLY);
struct settings settings_load;
/* defaults */
settings->play[0] = '\0';
if(fh >= 0)
{ /* does file exist? */
/* basic consistency check */
if(rb->filesize(fh) == sizeof(*settings))
{
rb->read(fh, &settings_load, sizeof(struct settings));
if (settings_load.crc == crc_settings(&settings_load))
{
rb->memcpy(settings, &settings_load, sizeof(struct settings));
}
else
rb->splashf(HZ * 5, "CRC Check Failed %s", filename);
}
else
rb->splashf(HZ * 5, "Incorrect File Size %s", filename);
rb->close(fh);
}
}
void save_settings(struct settings * settings, char* filename)
{
int fd = rb->creat(filename, 0666);
settings->crc = crc_settings(settings);
if(fd >= 0)
{ /* does file exist? */
rb->write (fd, settings, sizeof(*settings));
rb->close(fd);
}
}
void scroll_table_rollover(int * p_sel, int entries, int direction)
{
int sel = *p_sel;
if (direction < 0)
(sel)--;
else
(sel)++;
*p_sel = clamp_int_rollover(sel, 0, entries - 1);
}
static bool play_load(const char* filename)
{
struct browse_context browse;
if (rb->file_exists(filename))
{
rb->browse_context_init(&browse, 0, BROWSE_RUNFILE, "", 0, filename, "");
rb->rockbox_browse(&browse);
return true;
}
else
rb->splashf(HZ * 2, "Error: %s Does Not Exist", filename);
return false;
}
static bool play_picker(struct settings *settings)
{
bool ret = false;
struct browse_context browse;
rb->browse_context_init(&browse, SHOW_PLAYLIST,
BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
"Play", Icon_Menu_setting, "/", "");
browse.buf = settings->play;
browse.bufsize = sizeof(settings->play);
rb->rockbox_browse(&browse);
if (browse.flags & BROWSE_SELECTED)
{
rb->splashf(HZ * 2, "%s", settings->play);
if (rb->file_exists(settings->play))
ret = true;
//play_load("/test.txt");
}
return ret;
}
static void do_set_volume_table_values(struct settings *set)
{
/* user can elect to use the current volume level as the start or end volume */
int vol = rb->sound_get_current(SOUND_VOLUME);
int vol_min = rb->sound_min(SOUND_VOLUME);
int vol_max = rb->sound_max(SOUND_VOLUME);
set->table[M_STARTVOL] = do_select_clamped(set->table[M_STARTVOL], vol,
set->table[M_USECURVOL] == CURVOL_START,
vol_min, vol_max);
set->table[M_ENDVOL] = do_select_clamped(set->table[M_ENDVOL], vol,
set->table[M_USECURVOL] == CURVOL_END,
vol_min, vol_max);
}
static void do_set_action_table_values(struct settings *set)
{
if (set->table[M_TRIG1] == TRIG_NONE)
set->table[M_ACT1] = ACT_NONE;
if (set->table[M_TRIG2] == TRIG_NONE)
set->table[M_ACT2] = ACT_NONE;
if (set->table[M_TRIG3] == TRIG_NONE)
set->table[M_ACT3] = ACT_NONE;
}
static void init_table_settings(struct settings *set)
{
do_set_volume_table_values(set);
do_set_action_table_values(set);
set->table[M_SAVE] = (set->crc == crc_settings(set))? SAV_SAVED : SAV_SAVE;
}
static void event_callback(unsigned short id, void *param)
{
static int last_audio = AUDIO_STATUS_ERROR;
static int last_screen = 0;
static bool backlight_on = true;
int audio_status;
int screen;
int last_vol_change;
if (((struct track_event *)param)->flags & TEF_REWIND)//TEF_AUTO_SKIP
return; /* Not a true track end */
switch(id)
{
case PLAYBACK_EVENT_START_PLAYBACK:
triggered_events |= WATCHED_EV(TRIG_EVENT_START_PLAYBACK);
break;
case PLAYBACK_EVENT_TRACK_SKIP:
if (!(((struct track_event *)param)->flags & TEF_AUTO_SKIP))
triggered_events |= WATCHED_EV(TRIG_EVENT_TRACK_SKIP);
break;
case PLAYBACK_EVENT_TRACK_CHANGE:
triggered_events |= WATCHED_EV(TRIG_EVENT_TRACK_CHANGE);
break;
case PLAYBACK_EVENT_TRACK_FINISH:
if (!(((struct track_event *)param)->flags & TEF_REWIND))//TEF_AUTO_SKIP
triggered_events |= WATCHED_EV(TRIG_EVENT_TRACK_FINISH);
break;
case SELF_EVENT_FINALVOL_REACHED:
triggered_events |= WATCHED_EV(TRIG_EVENT_VOL_FINAL);
break;
case SELF_EVENT_TIMEOUT_REACHED:
triggered_events |= WATCHED_EV(TRIG_EVENT_TIMEOUT);
break;
case SELF_EVENT_CHECK_TRIGGERED:
last_vol_change = rb->global_status->last_volume_change;
if (last_vol_change + TIMEOUT_VOL_CHANGE < *rb->current_tick)
triggered_events |= WATCHED_EV(TRIG_EVENT_VOL_CHANGED
-
Okay, thanks for your answer - there is no rush (it is not a bug and with the lua script there is already something that works good enough).
Still it is great to see new developments, not everyone is reading the forum so it would be nice to have it out of the box especially for these users.
As mentioned before script is working fine for me (and continuous to be incredibly helpful)
I was just curious about the status - maybe I will celebrate existence of the script with a yearly friendly status update request ;)
-
fade to sleep plugin can now be found in plugins/demos/lua_scripts
-
Haven't checked this thread for a while, but nice to see the script included. Still using it - almost daily.
-
I'm pretty sure I edited it to include the new sleep timer functionality and ability to read the starting volume level
-
Indeed you did :) works nicely. That makes it perfect! Thanks.
For easy access I added the previous script to my shortcuts - that doesn't seem to work with this script/version. Updated yesterday to the latest dev version.
tried:
[shortcut]
type: browse
data: /rocks/demos/lua_scripts/fade2sleep.lua
name: fade2sleep
copied fade2sleep.lua to root
[shortcut]
type: browse
data: /fade2sleep.lua
name: fade2sleep
and tried data type (also some other variants - all resulted in 'no file')
[shortcut]
type: data
data: fade2sleep
name: fade2sleep
-
@Bilgus, maybe you missed the 'question' part of my reply? Would be nice to have easier access to the script via a shortcut. Is that possible?
-
I don't think lua scripts work through shortcuts you might try adding the lua_scripts.lua script to shortcuts and see if that works
-
I don't think lua scripts work through shortcuts you might try adding the lua_scripts.lua script to shortcuts and see if that works
Thx. Will give that a try then. BTW Your previous script worked fine through a shortcut.