Rockbox General > Rockbox General Discussion
Why the ipod 4G/5G scroll acceleration patch is jumpy, and how you might fix it
TP Diffenbach:
One (of several) reasons the scroll acceleration is jumpy is that I never got around to parameterizing part of it; I thought I'd be available to do this, but have been away from my PC (on a contract job) for nearly six months.
Check out line 154 of the patch as maintained by senab (Chris Barnes) (and note this is entirely my fault, not his, senab's done a great job maintaining the patch in my absence; the offending line was authored by me):
struct scroll_accel_jump scroll_accel_jumps[] = { { NOACCEL, 0, 0 }, { PAGE, 1, 0 }, { PAGE, 8, 0 } , { PERCENT, 10, 0 } };
Now, scroll accel has 4 (an arbitrary number that just happens to work) "levels": normal, "fast', "faster" and "fastest".
The line of code above assigns, in order normal to fastest, what happens when a particular level is reached. So at normal, no acceleration is performed, at fast acceleration the list is moved (forward or back) a page at a time, at faster 8 pages at a time, and at fastest, the list is moved 10% of its length.
"Page" means, the number of displayable lines on the screen, as set by the current font (and the presence/absence of the status bar). Change the font height, and you change the page length. So one page scrolls by the number of displayable lines on the screen.
It's the "faster" setting that is problematic: for long lists (like my 8600 tracks), 8 pages is great. For short lists, 8 pages is too much. And, of course, font size influences page size, so the smaller the font, the greater the number of lines in eight pages.
So as you change themes, you'll find some work better or worse for you. Exactly how they work, in turn depends on how aggressively you "turn" the scroll wheel.
So there are a lot of factors interacting to determine whether scrolling feels smooth or choppy.
What I eventually want to do is to make the action taken by each threshold user-configurable. (Settings would then update the scroll_accel_jumps array.) I didn't do that originally, because the patch was (and is) an experiment, and because I absolutely abhor writing menu code.
As a short-term fix, I encourage source builders to play with different hard-coded values in that array. Note that the array is of struct scroll_accel_jump, and that (kludgy, I admit) the third member of that struct is used to /report/ the actual number of lines to moved at runtime. So hard-codeing that third member's value will have no affect.
But you /can/ change the first two members.
The first member, jump_type can be:
PAGE, which will get you paged jumps (that is, the number of lines to move will be so number of pages, e.g., some multiple of the current lines_per_page as determined by the font in use)
or
PERCENT, which will page by list size.
The second member sets the number of pages or the percentage.
Note also that there's some sanity checking; since a percentage value for a short list can be less than a page value, it is possible that, e.g., "fastest" (hardcoded as PERCENT) might specify a scroll by fewer lines than "fast" (hardcoded as PAGE). If so, the larger of the two is used is used.
And if the list is less than a screen (page) long, acceleration NEVER happens.
I'll give an example to clarify this. Given a list 200 lines (items) long with a screen size of 20 lines, a "fastest" scroll is 10%, or 20 lines. But a "fast" scroll is 8 pages, or 160 lines. In that case, if the scroll acceleration is "fastest", the actual number of lines moved will be the greater of the two, or 160 lines.
Note this also means that the first accel after normal, that is the "fast" accel, should probably ALWAYS be one page. And of course the "normal" should always be "NOACCEL". So it's just the "faster" and "fastest" we want to play with.
Now, here's what I suggest: let's get rid of my foolish 8 page scroll. Eight pages is just too choppy.
Let's either change the hardcoding to 4 pages:
struct scroll_accel_jump scroll_accel_jumps[] = { { NOACCEL, 0, 0 }, { PAGE, 1, 0 }, { PAGE, 4, 0 } , { PERCENT, 10, 0 } };
Or, make "faster" and "fastest" both percentages, e.g.,
struct scroll_accel_jump scroll_accel_jumps[] = { { NOACCEL, 0, 0 }, { PAGE, 1, 0 }, { PERCENT, 5, 0 } , { PERCENT, 10, 0 } };
I'm not going to update the patch, even though is a straightforward change, simply because I can't test it. But anyone who can build and test is encouraged to provide this change.
But if you want, give these a try and let me know how it works. Or if someone s interested enough to make the faster and fastest jumps user configurable, all the better.
Llorean:
Out of curiosity, what if your list is short enough that 10% is actually less than 8 pages? Say your list is a mere 70 pages total? Does this mean that if you somehow trigger the last one it'll actually go slower than the previous one? Perhaps there should be a safeguard against this (though making it use a percent for both faster and fastest seems likely to do the case).
elborak:
--- Quote from: Llorean on May 04, 2007, 04:10:09 AM ---Out of curiosity, what if your list is short enough that 10% is actually less than 8 pages? Say your list is a mere 70 pages total? Does this mean that if you somehow trigger the last one it'll actually go slower than the previous one? Perhaps there should be a safeguard against this (though making it use a percent for both faster and fastest seems likely to do the case).
--- End quote ---
I think he covered this case:
--- Quote ---Note also that there's some sanity checking; since a percentage value for a short list can be less than a page value, it is possible that, e.g., "fastest" (hardcoded as PERCENT) might specify a scroll by fewer lines than "fast" (hardcoded as PAGE). If so, the larger of the two is used is used.
And if the list is less than a screen (page) long, acceleration NEVER happens.
--- End quote ---
TP Diffenbach:
--- Quote from: Llorean on May 04, 2007, 04:10:09 AM ---Out of curiosity, what if your list is short enough that 10% is actually less than 8 pages? Say your list is a mere 70 pages total? Does this mean that if you somehow trigger the last one it'll actually go slower than the previous one?
--- End quote ---
But that's the example I gave. The sanity check ensures that the largest* jump less than or equal to your scroll accell factor is made.
* Actually, that's not strictly true, but effectively is true. We actually only check until a larger factor's scroll is greater than a smaller factor's scroll. We assume the order of actions for fast, faster, fastest isn't pathological.
Below, "jump" means the number of lines in the list that we move.
That is, if scroll factor is "fastest", our initial candidate jump is fastest's jump.
We will execute "fastest's" jump unless "faster's" jump is greater than "fastest's" jump.
If "faster's" jump is greater than "fastest's", then faster becomes our new candidate, and we repeat, comparing that jump against "fast's" jump, and taking the larger of those two.
If the scroll accel factor is "faster", our initial candidate jump is the "faster" jump, which is compare against the "fast" jump, ertc.
If the scroll accel factor is "fast", we just use the "fast" jump.
This is easier to explain by just showing the code, so long as I point out that I'm using the kludge of recording teh actual number of lines to jump in the same struct that provides teh type (PAGE, PERCENT) and extent of the jump. Here's the code, annotated:
--- Code: ---static struct scroll_accel_jump* find_accel_for_list(
struct gui_list* list, struct scroll_accel_jump* jump, unsigned int raw_accel ) {
// second parameter is the array of jump policies
// third is the accell factor, [0..3]
// find items in the list
unsigned int l_items = list_items( list );
// find the number of displayable lines on the screen
unsigned int s_lines = screen_lines( list );
// accel factor == 0 == no accel
/* never accel in a list 1 it is "faster" or "fastest"
// so calculate the next lower jump and compare them
// keep doing so until you've compared to "faster" to "fast"
// if the lower accel's jump is bigger, decrement the accel factor
while( raw_accel > 1 ) {
calculate_jump_lines( jump + raw_accel - 1, l_items, s_lines );
if( jump[ raw_accel ].lines > jump[ raw_accel - 1 ].lines ) break;
--raw_accel;
}
// return the jump, which is the biggest jump for our accel factor
// that element has been modified to also contain
// the actual line count of the jump
// so we can give the caller the actual jump and the policy that produced it
// in case the caller wants to do something clever.
// of course in any OO language this would be terrible tight coupling
// but this is embedded C, different rules apply
return jump + raw_accel;
}
--- End code ---
senab:
I'm going to have a look at making the setting user configurable. I don't know that much C but i'll get by. I'll keep you posted ;D
//EDIT: Thomas, check your PM's.
Navigation
[0] Message Index
[#] Next page
Go to full version