mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-09 23:53:28 -04:00
skin_engine: hold option for the peak meter bars
The %pL and %pR bars followed every drop of the level, and had nothing like the peak marker of the built-in peak meter. With the new "hold" bar option a bar falls back at the peak meter's release rate, and the highest level is held for the peak hold time, both from the same settings as the built-in meter (peak_meter_get_times()). Without the option the bars are as before. The held peak is a block ending at the held level, 1 pixel thick or as many as the optional number after "hold" says (eg. "hold, 4"). On a bar with a fill image the block is drawn from the same part of the image, so a bar drawn as LED segments holds a whole segment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Change-Id: I5c6262ba02b7d2df6c5f80f15d4a1d4354ef8e26
This commit is contained in:
parent
70c5a4e9e9
commit
7d090f24b9
6 changed files with 139 additions and 2 deletions
|
|
@ -170,6 +170,34 @@ void ab_draw_markers(struct screen * screen, int capacity,
|
|||
|
||||
#endif
|
||||
|
||||
/* A peak meter bar with the hold option: the level falls back by the peak
|
||||
* release rate - pixels per tick, here of a bar of the given size - and
|
||||
* the highest recent level is held for the peak hold time. */
|
||||
static unsigned long peak_bar_hold(struct progressbar *pb, unsigned long end,
|
||||
int size)
|
||||
{
|
||||
int release, hold_ticks;
|
||||
long delta = current_tick - pb->last_tick;
|
||||
int level = end;
|
||||
|
||||
peak_meter_get_times(&release, &hold_ticks);
|
||||
pb->last_tick = current_tick;
|
||||
if (size <= 0)
|
||||
size = 1;
|
||||
if (delta >= 0 && delta < HZ)
|
||||
level = MAX(level, pb->level - delta * release * MAX_PEAK / size);
|
||||
pb->level = level;
|
||||
|
||||
if (TIME_AFTER(current_tick, pb->hold_tick))
|
||||
pb->hold_pos = 0;
|
||||
if (level > pb->hold_pos)
|
||||
{
|
||||
pb->hold_pos = level;
|
||||
pb->hold_tick = current_tick + hold_ticks;
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
void draw_progressbar(struct gui_wps *gwps, struct skin_viewport* skin_viewport,
|
||||
int line, struct progressbar *pb)
|
||||
{
|
||||
|
|
@ -230,6 +258,8 @@ void draw_progressbar(struct gui_wps *gwps, struct skin_viewport* skin_viewport,
|
|||
val = pb->type == SKIN_TOKEN_PEAKMETER_LEFTBAR ? left : right;
|
||||
length = MAX_PEAK;
|
||||
end = peak_meter_scale_value(val, length);
|
||||
if (pb->hold)
|
||||
end = peak_bar_hold(pb, end, pb->horizontal ? width : height);
|
||||
}
|
||||
else if (pb->type == SKIN_TOKEN_PLAYLIST_PERCENTBAR)
|
||||
{
|
||||
|
|
@ -345,6 +375,59 @@ void draw_progressbar(struct gui_wps *gwps, struct skin_viewport* skin_viewport,
|
|||
length, 0, end, flags);
|
||||
}
|
||||
|
||||
/* the held peak of a peak meter bar: a block hold_size pixels thick
|
||||
* ending at the held level, cut from the fill image if the bar has one,
|
||||
* so that a bar drawn as LED segments holds a segment */
|
||||
if (pb->hold && pb->hold_pos > 0 &&
|
||||
(pb->type == SKIN_TOKEN_PEAKMETER_LEFTBAR ||
|
||||
pb->type == SKIN_TOKEN_PEAKMETER_RIGHTBAR))
|
||||
{
|
||||
struct gui_img *img = pb->nobar ? NULL :
|
||||
SKINOFFSETTOPTR(get_skin_buffer(gwps->data), pb->image);
|
||||
int hx = x, hy = y, hw = width, hh = height;
|
||||
int len, pos, off, size = pb->hold_size;
|
||||
|
||||
if (!img && !(flags&BORDER_NOFILL))
|
||||
{
|
||||
/* inside the frame, like the bar */
|
||||
hx++; hy++; hw -= 2; hh -= 2;
|
||||
}
|
||||
len = (flags&HORIZONTAL) ? hw : hh;
|
||||
pos = len * pb->hold_pos / MAX_PEAK;
|
||||
off = pos - size;
|
||||
if (off < 0)
|
||||
{
|
||||
size += off;
|
||||
off = 0;
|
||||
}
|
||||
if (flags&INVERTFILL)
|
||||
off = len - pos;
|
||||
|
||||
if (flags&HORIZONTAL)
|
||||
{
|
||||
hx += off;
|
||||
hw = size;
|
||||
}
|
||||
else
|
||||
{
|
||||
hy += off;
|
||||
hh = size;
|
||||
}
|
||||
|
||||
if (img)
|
||||
{
|
||||
/* the image maps onto the bar 1:1, as in the bar itself */
|
||||
int bx = hx - x, by = hy - y;
|
||||
img->bm.data = core_get_data(img->buflib_handle);
|
||||
hw = MIN(hw, img->bm.width - bx);
|
||||
hh = MIN(hh, img->bm.height - by);
|
||||
if (hw > 0 && hh > 0)
|
||||
display->bmp_part(&img->bm, bx, by, hx, hy, hw, hh);
|
||||
}
|
||||
else if (hw > 0 && hh > 0)
|
||||
display->fillrect(hx, hy, hw, hh);
|
||||
}
|
||||
|
||||
if (SKINOFFSETTOPTR(get_skin_buffer(gwps->data), pb->slider))
|
||||
{
|
||||
int xoff = 0, yoff = 0;
|
||||
|
|
|
|||
|
|
@ -998,6 +998,10 @@ static int parse_progressbar_tag(struct skin_element* element,
|
|||
pb->nofill = false;
|
||||
pb->noborder = false;
|
||||
pb->nobar = false;
|
||||
pb->hold = false;
|
||||
pb->hold_size = 1;
|
||||
pb->level = pb->hold_pos = 0;
|
||||
pb->last_tick = pb->hold_tick = 0;
|
||||
pb->image = PTRTOSKINOFFSET(skin_buffer, NULL);
|
||||
pb->slider = PTRTOSKINOFFSET(skin_buffer, NULL);
|
||||
pb->backdrop = PTRTOSKINOFFSET(skin_buffer, NULL);
|
||||
|
|
@ -1083,14 +1087,16 @@ static int parse_progressbar_tag(struct skin_element* element,
|
|||
{
|
||||
eINVERT = 0, eNOFILL, eNOBORDER, eNOBAR, eSLIDER, eIMAGE,
|
||||
eBACKDROP, eVERTICAL, eHORIZONTAL, eNOTOUCH, eSETTING, eSETTING_OFFSET,
|
||||
e_PB_TAG_COUNT
|
||||
eHOLD, e_PB_TAG_COUNT
|
||||
};
|
||||
|
||||
static const char *pb_options[e_PB_TAG_COUNT + 1] = {[eINVERT] = "invert",
|
||||
[eNOFILL] = "nofill", [eNOBORDER] = "noborder", [eNOBAR] = "nobar",
|
||||
[eSLIDER] = "slider", [eIMAGE] = "image", [eBACKDROP] = "backdrop",
|
||||
[eVERTICAL] = "vertical", [eHORIZONTAL] = "horizontal",
|
||||
[eNOTOUCH] = "notouch", [eSETTING] = "setting", [eSETTING_OFFSET] = "soffset", [e_PB_TAG_COUNT] = NULL};
|
||||
[eNOTOUCH] = "notouch", [eSETTING] = "setting",
|
||||
[eSETTING_OFFSET] = "soffset", [eHOLD] = "hold",
|
||||
[e_PB_TAG_COUNT] = NULL};
|
||||
int pb_op;
|
||||
|
||||
while (curr_param < element->params_count)
|
||||
|
|
@ -1108,6 +1114,32 @@ static int parse_progressbar_tag(struct skin_element* element,
|
|||
pb->noborder = true;
|
||||
else if (pb_op == eNOBAR)
|
||||
pb->nobar = true;
|
||||
else if (pb_op == eHOLD)
|
||||
{
|
||||
pb->hold = true;
|
||||
/* optional: the thickness of the held peak */
|
||||
if (curr_param+1 < element->params_count)
|
||||
{
|
||||
struct skin_tag_parameter *next = param + 1;
|
||||
int size = -1;
|
||||
if (next->type == INTEGER)
|
||||
size = next->data.number;
|
||||
else if (next->type == STRING)
|
||||
{
|
||||
text = SKINOFFSETTOPTR(skin_buffer, next->data.text);
|
||||
if (isdigit(*text))
|
||||
size = atoi(text);
|
||||
}
|
||||
if (size >= 0)
|
||||
{
|
||||
if (size == 0)
|
||||
return WPS_ERROR_INVALID_PARAM;
|
||||
pb->hold_size = size;
|
||||
curr_param++;
|
||||
param++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pb_op == eSLIDER)
|
||||
{
|
||||
if (curr_param+1 < element->params_count)
|
||||
|
|
|
|||
|
|
@ -136,6 +136,12 @@ struct progressbar {
|
|||
bool nofill;
|
||||
bool noborder;
|
||||
bool nobar;
|
||||
/* peak meter bars: fall back at the peak release rate and mark the
|
||||
* held peak, as the recording screen's peak meter does */
|
||||
bool hold;
|
||||
int hold_size; /* thickness of the held peak, in pixels */
|
||||
int level, hold_pos; /* in units of MAX_PEAK */
|
||||
long last_tick, hold_tick;
|
||||
OFFSETTYPE(struct gui_img *) slider;
|
||||
|
||||
OFFSETTYPE(struct gui_img *) backdrop;
|
||||
|
|
|
|||
|
|
@ -492,6 +492,14 @@ void peak_meter_init_times(int release, int hold_ms, int clip_hold_sec)
|
|||
pm_clip_hold = HZ * clip_hold_sec;
|
||||
}
|
||||
|
||||
/* The release, in pixels per tick, and the peak hold time in ticks, as set
|
||||
* by peak_meter_init_times(): for peak meters drawn elsewhere */
|
||||
void peak_meter_get_times(int *release, int *hold_ticks)
|
||||
{
|
||||
*release = pm_peak_release;
|
||||
*hold_ticks = pm_peak_hold;
|
||||
}
|
||||
|
||||
#ifdef HAVE_RECORDING
|
||||
/**
|
||||
* Enable/disable clip counting
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ extern void peak_meter_set_clip_hold(int time);
|
|||
extern void peak_meter_peek(void);
|
||||
extern void peak_meter_init_range( bool dbfs, int range_min, int range_max);
|
||||
extern void peak_meter_init_times(int release, int hold_ms, int clip_hold_sec);
|
||||
extern void peak_meter_get_times(int *release, int *hold_ticks);
|
||||
#ifdef HAVE_AGC
|
||||
extern void peak_meter_get_peakhold(int *peak_left, int *peak_right);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -810,6 +810,13 @@ display cycling round the defined sublines. See
|
|||
specify an image.
|
||||
\item[nobar] -- don't draw the bar or its frame (for use with the
|
||||
``slider'' option).
|
||||
\item[hold] -- for the peak meter bars (\config{\%pL}, \config{\%pR}):
|
||||
the bar falls back at the peak meter's release rate instead of
|
||||
following each drop, and the highest level is held for the peak hold
|
||||
time. The next param may be a number, the thickness of the held peak in
|
||||
pixels (default 1). With a fill image the held peak is drawn from that
|
||||
part of the image, so for a bar drawn as LED segments set it to the size
|
||||
of one segment.
|
||||
\opt{touchscreen}{
|
||||
\item[notouch] -- don't create the touchregion for progress/volume bars.
|
||||
\item[touchlabel] -- the next option sets the label of the bar touch area, for linking with the \%Tl tag.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue