Have you ever played with an app that has volume control and thought that it really didn't work the way you expected? You start at full volume and move the slider down. At about half way you start to notice a difference, and then you start to loose control all together. You never get it to the exact volume you want. It's either too low or too high.
I have experienced this and it is rather annoying! When I started using volume controls in apps I've developed for Musicopoulos, I wanted things to work much better. So I went on a journey of discovery. Like many learning adventures you come across when programming, I discovered something I would never have otherwise come across.
The frustration I was experiencing with volume control in many apps wasn't related to broken sliders. The logic behind the volume control wasn't developed with the ear in mind. Most people implement volume as a liner function. For example, if you have a slider with a range of values between 0.0 and 1.0, you set the volume to whatever position the slider is on.
This is not the way it should be done, buy why?
As it turns out, we hear things on a logarithmic scale, not a linear scale.What this means is that our ears are more sensitive to changes at low volumes than they are at high volumes. That's just the way our ears have evolved over time, and allows us to experience a dynamic range of sound levels.
If we apply this thought process to our linear volume control, we don't perceive much of a change between say 0.5 and 1.0, however, between 0.0 and 0.5 we hear very minor changes in volume. This means that we need finer control over lower volumes, and less control over higher volumes.
So what is the solution here? We need to define an exponential function for managing our volume. Since our ears process sound logarithmically, using an exponential function to control sound will give us the perception of a linear change in sound. Here is some math to prove that:
log(e^x) = x
What is this equation saying for us? Think of it like this:
- x is the volume we set on our slider, let's say 0.5
- We change this value exponentially and set the volume to e^0.5
- Our ear processes this volume logarithmically, so we perceive a volume of log(e^0.5)
- The result of log(e^0.5) is 0.5!
There are a couple of challenges with exponential functions:
- An exponential decay function will never reach zero.
- Controlling the shape of an exponential curve can be tricky.
Yes, would could deal with these challenges and come up with a perfect solution, but we don't have to. We can make a fair approximation using a simpler equation:
y = x^4
- x is the selected volume on your slider
- y is the 'perceived' volume by our ears
As it turns out, this is a good approximation of the ideal exponential curve for our volume control. When implemented in your apps, you will see that you gain fine control over sound levels in the lower range, exactly where you need it.
The Sample Project
I have put together a very quick sample project that you can use to see just how different each solution is. It's a very simple app. It just loops a music track (just a bunch of loops I put together in GarageBand) and you can control the volume using either a linear or (almost) exponential calculation.
When you play with the volume sliders, watch how changes on 1 affects the other. You can clearly see how much more control you have with the (almost) exponential solution.
The project is available for download on GitHub,https://github.com/OhNo789/VolumeControl, and you are free to use it any way you like.