This is due to an unfortunate android bug with how volumes were applied to playing voices in the android audio implementation. The bug arose out of a misunderstanding of how to properly convert linear volume to dB volume.
Ais the currently released android code that causes the problem (and that you found):
// Convert volume to millibels.
SLmillibel MaxMillibel = 0;
SLmillibel MinMillibel = -3000;
(*SL_VolumeInterface)->GetMaxVolumeLevel( SL_VolumeInterface, &MaxMillibel );
SLmillibel VolumeMillibel = (Volume * (MaxMillibel - MinMillibel)) + MinMillibel;
VolumeMillibel = FMath::Clamp(VolumeMillibel, MinMillibel, MaxMillibel);
SLresult result = (*SL_VolumeInterface)->SetVolumeLevel(SL_VolumeInterface, VolumeMillibel);
check(SL_RESULT_SUCCESS == result);
This algorithm takes the linear volume and scales it between a min and max decibel (-30 dB and 0) and is definitely not
how to do it.
Not only is it just wrong (the output volumes are not equivalent to the input volumes) but the min volume value (-30 dB)
will still be audible (as you discovered) if you crank your output volume. -30 dB is equivalent to a linear volume/magnitude of 0.032.
It is also not correct to just change the min volume in that clamp to -90 dB since (as mentioned above) it will result in incorrect volumes. For
example, if the input linear volume was 0.5, scaling this between -90 dB and 0 dB would be -45 dB! When in reality a 0.5 linear
volume is actually -6 dB and (as we all know) totally audible.
I recently checked in a fix to this, which properly converts linear volume to dB volume using the correct equation for
converting between linear and dB volumes:
dBVolume = 20 * Log10(Linear)
And of course, going in reverse, solving for linear volume (if you want to confirm a given dB value results in the expected linear value):
Linear = pow(dBVolume / 20, 10)
Unfortunately, a linear volume of 0.0 will result in a hugely negative number (technically negative infinity) so we still
need to clamp it – but instead of clamping at an audible -30 dB, I am clamping it at an absolutely inaudible -120 dB.
static const int64 MinVolumeMillibel = -12000;
if (Volume > 0.0f)
{
// Convert volume to millibels.
SLmillibel MaxMillibel = 0;
(*SL_VolumeInterface)->GetMaxVolumeLevel(SL_VolumeInterface, &MaxMillibel);
SLmillibel VolumeMillibel = (SLmillibel)FMath::Clamp<int64>((int64)(2000.0f * log10f(Volume)), MinVolumeMillibel, (int64)MaxMillibel);
SLresult result = (*SL_VolumeInterface)->SetVolumeLevel(SL_VolumeInterface, VolumeMillibel);
check(SL_RESULT_SUCCESS == result);
}
else
{
SLresult result = (*SL_VolumeInterface)->SetVolumeLevel(SL_VolumeInterface, MinVolumeMillibel);
check(SL_RESULT_SUCCESS == result);
}
This checked into mainline on github here:
https://github.com/EpicGames/UnrealEngine/commit/418b93bbb4c3d13e0b9e9e926117d739ab721ff3