plugin lrcplayer: Added manual scrolling feature

lrcplayer currently automatically scrolls the lyrics for you, as it assumes that there is sync information in the lyrics. In the case where there aren't (like for ID3V2 USLT tags), the plugin will automatically assume timestamps and will scroll automatically.
This commit adds an option in the Display Menu to allow automatic scrolling to be disabled. When disabled, you can scroll the lyrics using the volume keys (like you would on the iPod's OS).

Change-Id: I87ef27fd2b84c97374bdfd0e47f4c00ddb4bc85e
This commit is contained in:
Lourenço Soares 2026-02-19 20:02:12 +00:00 committed by Solomon Peachy
parent 8013aa2370
commit 0b3a5ab1e6
2 changed files with 74 additions and 32 deletions

View file

@ -74,6 +74,7 @@ struct preferences {
bool wrap;
bool wipe;
bool active_one_line;
bool autoscroll;
int align; /* 0: left, 1: center, 2: right */
bool statusbar_on;
bool display_title;
@ -99,6 +100,7 @@ static const char *extentions[NUM_TYPES] = {
static struct lrc_info {
struct mp3entry *id3;
long elapsed;
long scroll;
long length;
long ff_rewind;
int audio_status;
@ -1643,21 +1645,25 @@ static int display_lrc_line(struct lrc_line *lrc_line, int ypos, int i)
struct screen *display = rb->screens[i];
struct lrc_word *lrc_word;
struct lrc_brpos *lrc_brpos;
long time_start, time_end, elapsed;
long time_start, time_end, time_elapsed, elapsed;
int count, width, nword;
int xpos;
const char *str;
bool active_line;
if (!prefs.autoscroll)
time_elapsed = current.scroll;
else
time_elapsed = current.elapsed;
time_start = get_time_start(lrc_line);
time_end = get_time_start(lrc_line->next);
active_line = (time_start <= current.elapsed
&& time_end > current.elapsed);
active_line = (time_start <= time_elapsed
&& time_end > time_elapsed);
if (!lrc_line->width)
{
/* empty line. draw bar during interval. */
long rin = current.elapsed - time_start;
long rin = time_elapsed - time_start;
long len = time_end - time_start;
if (current.wipe && active_line && len >= 3500)
{
@ -1697,12 +1703,12 @@ static int display_lrc_line(struct lrc_line *lrc_line, int ypos, int i)
else
time_end = get_time_start(lrc_line->next);
if (time_start > current.elapsed || !active_line)
if (time_start > time_elapsed || !active_line)
{
/* inactive */
elapsed = 0;
}
else if (!current.wipe || time_end <= current.elapsed)
else if (!current.wipe || time_end <= time_elapsed)
{
/* active whole word */
elapsed = lrc_word->width;
@ -1710,7 +1716,7 @@ static int display_lrc_line(struct lrc_line *lrc_line, int ypos, int i)
else
{
/* wipe word */
long rin = current.elapsed - time_start;
long rin = time_elapsed - time_start;
long len = time_end - time_start;
elapsed = rin * lrc_word->width / len;
}
@ -1727,6 +1733,8 @@ static int display_lrc_line(struct lrc_line *lrc_line, int ypos, int i)
c = word_count;
w = word_width;
}
if (prefs.autoscroll)
{
if (elapsed <= 0)
{
set_to_inactive(display);
@ -1749,6 +1757,7 @@ static int display_lrc_line(struct lrc_line *lrc_line, int ypos, int i)
display->set_drawmode(DRMODE_INVERSEVID);
#endif
}
}
rb->strlcpy(temp_buf, str, c+1);
display->putsxy(xpos, ypos, temp_buf);
str += c;
@ -1777,13 +1786,18 @@ static int display_lrc_line(struct lrc_line *lrc_line, int ypos, int i)
static void display_lrcs(void)
{
long time_start, time_end, rin, len;
long time_start, time_end, time_elapsed, rin, len;
int nline[NB_SCREENS] = {0};
struct lrc_line *lrc_center = current.ll_head;
if (!lrc_center) return;
while (get_time_start(lrc_center->next) <= current.elapsed)
if (!prefs.autoscroll)
time_elapsed = current.scroll;
else
time_elapsed = current.elapsed;
while (get_time_start(lrc_center->next) <= time_elapsed)
{
nline[SCREEN_MAIN] += lrc_center->nline[SCREEN_MAIN];
#ifdef HAVE_REMOTE_LCD
@ -1794,7 +1808,7 @@ static void display_lrcs(void)
time_start = get_time_start(lrc_center);
time_end = get_time_start(lrc_center->next);
rin = current.elapsed - time_start;
rin = time_elapsed - time_start;
len = time_end - time_start;
struct screen *display;
@ -1810,9 +1824,9 @@ static void display_lrcs(void)
y = (nblines-1)/2;
if (rin < 0)
{
/* current.elapsed < time of first lrc */
/* time_elapsed < time of first lrc */
if (!current.wipe)
ypos = (time_start - current.elapsed)
ypos = (time_start - time_elapsed)
* font_ui_height / time_start;
else
y++;
@ -1849,7 +1863,7 @@ static void display_lrcs(void)
ypos = display_lrc_line(lrc_line, ypos, i);
lrc_line = lrc_line->next;
}
if (!lrc_line && ypos < vp_lyrics[i].height)
if (!lrc_line && ypos < vp_lyrics[i].height && prefs.autoscroll)
display->putsxy(0, ypos, "[end]");
display->update_viewport();
@ -2150,6 +2164,7 @@ static void load_or_save_settings(bool save)
{ TYPE_BOOL, 0, 1, { .bool_p = &prefs.wipe }, "wipe", NULL },
{ TYPE_BOOL, 0, 1, { .bool_p = &prefs.active_one_line },
"active one line", NULL },
{ TYPE_BOOL, 0, 1, { .bool_p = &prefs.autoscroll }, "autoscroll", NULL },
{ TYPE_INT, 0, 2, { .int_p = &prefs.align }, "align", NULL },
{ TYPE_BOOL, 0, 1, { .bool_p = &prefs.statusbar_on },
"statusbar on", NULL },
@ -2179,6 +2194,7 @@ static void load_or_save_settings(bool save)
prefs.wrap = true;
prefs.wipe = true;
prefs.active_one_line = false;
prefs.autoscroll = true;
prefs.align = 1; /* center */
prefs.statusbar_on = false;
prefs.display_title = true;
@ -2264,6 +2280,7 @@ static bool lrc_display_menu(void)
LRC_MENU_WIPE,
LRC_MENU_ALIGN,
LRC_MENU_LINE_MODE,
LRC_MENU_SCROLL_MODE,
};
int selected = 0;
@ -2271,7 +2288,8 @@ static bool lrc_display_menu(void)
MENUITEM_STRINGLIST(menu, "Display Settings", NULL,
"Wrap", "Wipe", "Alignment",
"Activate Only Current Line");
"Activate Only Current Line",
"Automatic Scrolling");
struct opt_items align_names[] = {
{"Left", -1}, {"Centre", -1}, {"Right", -1},
@ -2295,6 +2313,9 @@ static bool lrc_display_menu(void)
usb = rb->set_bool("Activate Only Current Line",
&prefs.active_one_line);
break;
case LRC_MENU_SCROLL_MODE:
usb = rb->set_bool("Automatic Scrolling", &prefs.autoscroll);
break;
case MENU_ATTACHED_USB:
usb = true;
break;
@ -2619,10 +2640,28 @@ static int handle_button(void)
}
break;
case ACTION_WPS_VOLDOWN:
if (!prefs.autoscroll)
{
current.scroll -= 3000;
if (current.scroll < 0)
current.scroll = 0;
}
else
{
rb->adjust_volume(-1);
}
break;
case ACTION_WPS_VOLUP:
if (!prefs.autoscroll)
{
current.scroll += 3000;
if (current.scroll > current.length)
current.scroll = current.length;
}
else
{
rb->adjust_volume(1);
}
break;
case ACTION_WPS_CONTEXT:
ret = LRC_GOTO_EDITOR;
@ -2705,6 +2744,7 @@ static int lrc_main(void)
{
current.elapsed = 0;
current.length = 1;
current.scroll = 0;
}
if (current.id3 && id3_timeout &&
@ -2782,6 +2822,7 @@ enum plugin_status plugin_start(const void* parameter)
current.lrc_file[0] = 0;
current.ff_rewind = -1;
current.found_lrc = false;
current.scroll = 0;
if (parameter && check_audio_status())
{
const char *ext;
@ -2790,7 +2831,7 @@ enum plugin_status plugin_start(const void* parameter)
rb->strcpy(current.lrc_file, parameter);
if (!rb->file_exists(current.lrc_file))
{
rb->splash(HZ, "Specified file dose not exist.");
rb->splash(HZ, "Specified file does not exist.");
return PLUGIN_ERROR;
}
ext = rb->strrchr(current.lrc_file, '.');

View file

@ -755,6 +755,7 @@ Sho Tanimoto
Nyx Guan
Arin Kim
Ingmar Steen
Lourenço Soares
The libmad team
The wavpack team