Open GoogleCodeExporter opened 8 years ago
Yup, most certainly an issue.
Fred posted an excellent description of the issue and a great link for
understanding what's going on. However, if you don't care to read up on the
math and just want to know what code to change, I did a custom build with the
following change:
NotificationService.java (line 396):
"MediaSingleton.INSTANCE.setVolume(start*start*start*start);"
That's it. No other changes are needed. Works like a charm! You could use
Math.pow() if you want as well. Makes my mornings so much nicer.
This is also a duplicate of issue 41, so you can close both with this super
simple change. :-)
Thanks for making a great app.
Original comment by jrdaw...@gmail.com
on 26 Jan 2014 at 3:31
[deleted comment]
[deleted comment]
Thanks jrdaw. I tried your modified line of code. It works very well.
As I tried it I found one more thing to change. The article I previously linked
to describes perception of sound as logarithmic, and points out that there is
no zero on a logarithmic scale, and that a minimum level must be set according
to the dynamic range of the sound system in use. As there rarely is a
specification of the sound system, the article suggests cheating by using the
simplified formula pow(volume, 4), which does have an absolute zero point.
When I tried there was nothing but noise when the volume is below 12.5%. I
believe this applies to all Android devices, as this is limited by the 16 bit
sample size of the audio. This means that it's not good to rely on the zero
point of the simplified formula. I recommend that the default setting of the
start fade volume is set to 12% instead of 0% as it is currently.
I seem to have missed the duplicate issue despite looking through all issues
before making this one.
Summary of suggested changes:
- Change NotificationService.java line 396 to:
MediaSingleton.INSTANCE.setVolume(start * start * start * start);
- Change AlarmSettings.java line 69 to: volumeStartPercent = 12;
Original comment by fred...@portstrom.com
on 29 Jan 2014 at 5:14
This post is long and you might not care to read it all... see the "Summary" at
the bottom for the short version.
Ok, I've spent some more time looking into this, and while in many ways I'm
just more confused, I do have what I think is a better answer.
First off, I spent a while looking into the 16 bit thing (which is correct) and
trying to figure out how a DAC (Digital to Analog Converter) works to see what
is happening in the phone. Learned a lot, not entirely sure how much of it was
useful.
Everyone agrees, humans perceive sound logarithmically, which means an
equivalent variation in input levels at low volumes (e.g. moving the dial by
10% on a linear scale) seems like large volume change versus the same variation
at high volumes. How to deal with that seems to be not so simple.
I found tons of people talking about this issue online, and tons of different
formulas people use to address this issue. The original linked article
suggests volume=e^(6.908*x) / 1000, or it's close approximation, x^4. As Fred
pointed out, this particular function has problems at the low end of the scale,
where anything under 12% volume doesn't even register as sound.
The reason for there being so many different formulas is not exactly clear,
though my guess is that perceived sound volume is somewhat relative (everyone
hears a bit differently), and what people expect of a volume slider also
varies. Some functions provide better control at high volumes, some better
control at low volumes. However, this is just my personal speculation.
Personally, I imagine a slider having equal control at all volumes. What does
that mean exactly, I'm not sure. However, I would like to feel that adjusting
the slider by 5% anywhere (either at low or high volumes) feel like a slight
and equivalent increase in volume.
After watching some Khan Academy videos to refresh my memory on logarithms and
log-scale plotting, I calculated my own formula (which turns out to be one of
the ones I found elsewhere). The formula is: (10^x -1 )/9, and doesn't
require taking into account things like the dynamic range of the device, but
when I listen to it, seems to be exactly what I would expect a volume control
to sound like. I will attempt to explain how I came about this formula:
If you imagine a piece of horizontal log paper, with 10 vertical lines
representing the numbers 1 to 10 (0 never exists on log paper), you first start
with a large gap between line 1 and 2, then progressively smaller gaps until
you have lines 9 and 10 almost touching. This is a log scale. I can take the
line labeled 1 to represent my lowest volume (i.e. no sound), and 10 to
represent maximum volume. To find out what 50% volume means (i.e. half-way
between the lines 1 (which is 10^0) and 10 (which is 10^1)), I can use the
formula 10^0.5, which gives me 3.16. So, halfway between the 1 and the 10 is
slightly right of 3. Cool! So I can get the location of any volume percentage
x, by using the formula 10^x. This will give me values between 1 and 10.
Unfortunately, the Android MediaPlayer requires values between 0 and 1, so we
need to translate our formula to produce the appropriate values. To do this,
we first subtract 1 (i.e. 10^x - 1), giving us values that range from 0 to 9.
Then we divide by 9, giving us values from 0 to 1. The final formula is (10^x
-1)/9.
When I use this formula, I hear sound immediately (i.e. 1% volume is very low,
but still audible), and the increase in volume is noticeable and (to my ears at
least) roughly equal along the entire scale.
This formula requires the Math library, which isn't a big issue here because it
is already loaded. Of course, if you wanted, you could generate a polynomial
using the least-square method to get a very similar function without needing
the Math library. Some online web page that computes the least-square gave me
this function: 0.8x^3 - 0.1x^2 + 0.35x - 0.004. This approximation is
ridiculously similar, so can be used in place of the previous function.
SUMMARY of our understanding of this issue so far:
- *Any* logarithmic function for volume level is better than a linear one.
Using "start*start*start*start" will work just fine.
- I personally feel that "MediaSingleton.INSTANCE.setVolume( (float)(
(Math.pow(10, start)-1f)/9f ) );" is a nicer function.
Feel free to give them both a try and pick the one you like better.
Original comment by jrdaw...@gmail.com
on 30 Jan 2014 at 1:17
“The formula is: (10^x -1 )/9, and doesn't require taking into account
things like the dynamic range of the device”
Not true. The requirement to take into account the dynamic range cannot be
magically eliminated. You have arbitrarily selected a dynamic range from 10^0
to 10^1, and you could just as well have started at 10^-1 or ended at 10^2 to
get a higher dynamic range.
The formula 10 ^ x in the range 0 < x < 1 matches the approximation x ^ 4 in
the range .752781 < x < 1. It's easier to see that .752781 is an arbitrary
chosen number than the equal 10^0.
The difference this formula does is that the volume increases quicker in the
beginning. I've tried it. It causes the volume to increase too quickly in the
beginning for me. I still think that the best is to start at 12.5% volume and
call setVolume(start * start * start * start).
Original comment by fred...@portstrom.com
on 30 Jan 2014 at 12:55
Ahh, I stand partly corrected... Probably shouldn't post late at night.
The originally linked article uses the formula e^6.908x/1000. These are
seemingly odd numbers, so I converted from base 'e' to base 10. The equivalent
formula is then 10^3x / 1000, which in my mind is much easier to grasp. This
represents, using my log paper analogy, the space between 1 and 1000, whereas I
used the space between 1 and 10. My version of this same curve would be:
10^3x-1/999. These two formulas are essentially identical (the slight
difference being I make my formula go through (0,0) while the other doesn't but
gets really close).
Going back to the article shows that the 1-1000 range covers 60dB, where the
1-10 range covers 20dB. While I would be willing to agree my phone sucks, I
can't imagine is sucks so bad I only have a 20dB range, yet that formula does
seem the most equal to me.
A generic version of this formula is: (10^ax - 1)/(10^a -1). In my case, I
picked a=1, Fred likes a=3. It would appear that which value of 'a' you use
depends on the quality of your phone and your personal taste in what you think
sounds right.
You could, if you cared to, allow customization of the range by letting the
user select a value of 'a'. For an alarm clock, that might be overkill.
Even more overkill would be allowing the user to pick a 'floor'. This would be
the lowest value that produces sound on their phone (in Fred's case of 12.5% in
the x^4 formula is 0.00025). You could then adjust the formula to have both
the slope 'a' and the floor 'f', so that sound starts being produced
immediately yet increases comfortably per the device's range and user's
preference. This formula would be: (10^ax - 1)(1 - f)/(10^a - 1) + f.
Finally, after having woken up this morning to my alarm with slope a=1, I agree
it goes up in volume too fast. Now, if I were to create a volume slider for a
music player, I'd pick a=1, but for waking up, it seems I actually prefer a
non-linear increase in volume, so the quite music fades in more gradually, only
getting loud towards the end to make sure I'm awake.
So, what am I'm going to do... Since I've already got the guts of this app
open and compiling it is easy, I'm using my uber-overkill formula with a floor
of 0.0005 and a slope of 3, which I've determined works well on my phone and
for my preference in volume increase. After that, I'm going to stop worrying
about it and get on with my life (though it was both informative and fun doing
this, I don't plan on doing this forever, lol).
What I recommend as a code change in the app... Stick to
"start*start*start*start". Why? It works, it's easy, and users can pick a
starting volume using the existing interface if they're not happy with the
little bit of silence at the start.
Cheers,
Original comment by jrdaw...@gmail.com
on 30 Jan 2014 at 5:29
Original issue reported on code.google.com by
fred...@portstrom.com
on 7 Jan 2014 at 7:08