Android: MediaPlayer setVolume function

This function is actualy wonderful. Thanks to it you can create a volume scale with any number of steps!

Let's assume you want 50 steps:

int maxVolume = 50;

Then to set setVolume to any value in this range (0-49) you do this:

float log1=(float)(Math.log(maxVolume-currVolume)/Math.log(maxVolume));
yourMediaPlayer.setVolume(log1,log1); //set volume takes two paramater

Nice and easy! And DON'T use AudioManager to set volume! It will cause many side effects such as disabling silent mode, which will make your users mad!


Following user100858 solution I just post my exact code that works:

private final static int MAX_VOLUME = 100;
...
...
final float volume = (float) (1 - (Math.log(MAX_VOLUME - soundVolume) / Math.log(MAX_VOLUME)));
mediaPlayer.setVolume(volume, volume);

soundVolume is the volume you would like to set, between 0 and MAX_VOLUME. So between 0 and 100 in this example.


For Android MediaPlayer.setVolume, searching the web seems to show 0.0f for no sound, 1.0f for full sound.


The other answers here are not correct--or at least, they're not configured properly.

Perform the following test, using their code (e.g. that of Tomasz or ssuukk):

1) Set 100 as the "max volume"/number of steps, and submit the volume 50.

It returns: 0.150514997831991

2) Set 1000 as the "max volume"/number of steps, and submit the volume 500.

What does it return? The same value, 0.150514997831991, right?

Nope. Instead, it's: 0.100343331887994

In other words, the existing answers change how they scale the input volume-percent (i.e. the transformation curve) based on how many volume-steps you set.

I've spent the last few hours looking into this issue; enough that I don't feel like going into too much detail explaining the issue. Instead I'll just post the large code/comment block in my program concerning it. (it's in C#, for Xamarin Android, but the functionality should be the same for Java)

public enum VolumeScaleType
{
    //Energy, // what MediaPlayer possibly treats passed values as
    Amplitude, // what MediaPlayer most likely treats passed values as
    Loudness // what people treat everyday volume values as (as in "that sounded 2 times as loud")
}

// MediaPlayer
/*public static void SetVolume_IncorrectSOApproach(this MediaPlayer s, double volume, VolumeScaleType volumeType = VolumeScaleType.Loudness)
{
    const int maxVolume = 100;
    var volume_toScale = volume * maxVolume;
    double volume_scalar = volumeType == VolumeScaleType.Amplitude ? volume : (1 - (Math.Log(maxVolume - volume_toScale) / Math.Log(maxVolume)));
    s.SetVolume((float)volume_scalar, (float)volume_scalar);
}*/

public static void SetVolume_MyPossiblyCorrectApproach(this MediaPlayer s, double volume, VolumeScaleType volumeType = VolumeScaleType.Loudness)
{
    // Links:
    // 1) http://en.wikipedia.org/wiki/Decibel
    // 2) http://trace.wisc.edu/docs/2004-About-dB
    // 3) http://hyperphysics.phy-astr.gsu.edu/hbase/sound/loud.html
    // 4) http://www.animations.physics.unsw.edu.au/jw/dB.htm
    // 5) http://www.soundmaskingblog.com/2012/06/saved_by_the_bell
    // 6) http://www.campanellaacoustics.com/faq.html
    // 7) http://physics.stackexchange.com/questions/9113/how-sound-intensity-db-and-sound-pressure-level-db-are-related
    // 8) http://www.sengpielaudio.com/calculator-loudness.htm (note: page uses terms 'power/intensity' and 'pressure' differently; power/intensity: for whole shell at distance, pressure: field-quantity?)
    // basic idea: you can think of one decibel (of gain), + or -, as *translating into* the given changes-in/multipliers-for energy, amplitude, or loudness
    // (i.e. one decibel provides a specific amount to multiply energy, amplitude, and loudness values, such that they remain aligned realistically)
    // note: the 'one decibel' unit is set up to correspond roughly to a change in loudness just substantial enough to be noticeable
    // note: the 'quietest perceivable sound' example (standard) base has these absolute values: 'e' is 1 pico-watt per square-foot, 'a' is 20 micropascals, 'l' is the quietest-perceivable-loudness

    // references (for q.p.s. base)   | db (gain) | energy           | amplitude            | loudness
    // ===============================================================================================
    // actual silence                 | -inf      | 0                | 0                    | 0
    // (a seeming silence)            | -20       | e / 100          | a / 10               | 0 (would be l / 4, if 'l' weren't already for the quietest-perceivable-sound)
    // (a seeming silence)            | -10       | e / 10           | a / 3.16227/sqrt(10) | 0 (would be l / 2, if 'l' weren't already for the quietest-perceivable-sound)
    // quietest perceivable sound     | 0         | e                | a                    | l
    // ?                              | 1         | e * 1.258925     | a * 1.122018         | l * 1.071773
    // rustling leaves                | 10        | e * 10           | a * 3.16227/sqrt(10) | l * 2
    // whisper, or rural nighttime    | 20        | e * 100          | a * 10               | l * 4
    // watch ticking                  | 30        | e * 1000         | a * 31.622/sqrt(100) | l * 8
    // quiet speech, or rural daytime | 40        | e * 10000        | a * 100              | l * 16
    // dishwasher in next room        | 50        | e * 100000       | a * 316/sqrt(100000) | l * 32
    // ordinary conversation          | 60        | e * 1000000      | a * 1000             | l * 64
    // ===============================================================================================

    // assuming MediaPlayer.SetVolume treats passed values as Amplitude
    Func<double, double> convertLoudnessToAmplitude = loudness=>Math.Pow(10, Math.Log(loudness, 4));
    var volume_amplitude = volumeType == VolumeScaleType.Amplitude ? volume : convertLoudnessToAmplitude(volume);
    s.SetVolume((float)volume_amplitude, (float)volume_amplitude);
    // assuming MediaPlayer.SetVolume treats passed values as Energy
    //Func<double, double> convertLoudnessToEnergy = loudness=>Math.Pow(100, Math.Log(loudness, 4));
    //var volume_energy = volumeType == VolumeScaleType.Energy ? volume : convertLoudnessToEnergy(volume);
    //s.SetVolume((float)volume_energy, (float)volume_energy);
}

Conclusion

The documentation is sparse, so I can't know for sure if I have the right scaling-system/type-of-unit the SetVolume method expects.

Assuming it expects an Amplitude value, the code above may be the correct volume setting code for it. (taking desired Loudness, linear, as an input, and outputting/setting the Amplitude value needed for the built-in SetVolume method)

I'm not sure it's correct, though, and am too tired to confirm. If anyone has further thoughts, feel free to add them. (3+ hours is enough to spend on an issue like this, in one day)

Edit

After listening carefully, and comparing the loudness-fade effect by:

  1. Just submitting the desired loudness to the SetVolume method.
  2. Exponentiating (basically) the desired-loudness before sending it in, to make it an Amplitude (or the like) value that the SetVolume method says it expects.

I find that option 1 seems to be closer to a linear loudness fade-in! In other words... from actually listening and comparing the basic approach, with the various transformation approaches shown here, it seems the documentation is wrong and the SetVolume method does in fact just expect the loudness value on a linear scale. (perhaps they've updated it to work more intuitively in one of the recent API versions, but haven't updated the docs?)

If so, that sure makes it easy. That's what I'm going with for now. (though I'll keep the exponentiation/scale-fixing approach as a program setting, I suppose, just to have an excuse to keep some result of all that time invested!)