Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Logarithmic Volume Control #373

Open
jcadduono opened this issue Jun 19, 2021 · 7 comments
Open

Logarithmic Volume Control #373

jcadduono opened this issue Jun 19, 2021 · 7 comments
Labels
enhancement New feature or request

Comments

@jcadduono
Copy link

Is your feature request related to a problem? Please describe.
Controlling the volume is a struggle because there's almost no difference between 25% and 100% and most of the normal listening range is from 0-15% on my system.

Describe the solution you'd like
Add a volume control config option between linear and logarithmic.

Describe alternatives you've considered
Sending audio to a mixer with a max volume set at 25%, but then I wouldn't be able to blast music when wanted.

Additional context
I dunno maybe there's a better way to do this?

@jcadduono jcadduono added the enhancement New feature or request label Jun 19, 2021
@manfreddz
Copy link

Why was this closed? I can't find any option for logarithmic volume control..

@manfreddz
Copy link

manfreddz commented Sep 2, 2022

I think this would be a really nice addition. I've experimented with some manual code changes and adding the following row in AudioSink.setVolume() works well for me:

volumeNorm = (float) Math.pow(volumeNorm, 4) / 2;

I will run my custom build until this gets added (if ever). Could we reopen this issue? @jcadduono @devgianlu

@devgianlu devgianlu reopened this Sep 2, 2022
@jcadduono
Copy link
Author

sorry, the moment i found the rust version of librespot (with cubic volume control that works really well with my AVR) i flocked to that and closed this to pretend it never happened lol

@manfreddz
Copy link

No worries! I've run librespot for a few years, but it's just too buggy. This is the feature I'm missing in the Java version.

@tofublock
Copy link

tofublock commented Aug 22, 2023

I have the same problem, and this improves it greatly. Power of 4 moves it too much in the other direction for me (why did you choose that value, @manfreddz?) but pow(volumeNorm, 2) is very nice.
I'm not so sure about it mathematically, but if it works it works. :)

Would be great if this easy change could be integrated here!

I think this would be a really nice addition. I've experimented with some manual code changes and adding the following row in AudioSink.setVolume() works well for me:

volumeNorm = (float) Math.pow(volumeNorm, 4) / 2;

I will run my custom build until this gets added (if ever). Could we reopen this issue? @jcadduono @devgianlu

@dsheets
Copy link

dsheets commented Nov 29, 2023

I migrated from librespot-org/librespot for the local API and immediately had this very loud problem. My Java is approximately 16 years out-of-date but this is the patch I'm running now to get something very similar to librespot-org/librespot's default logarithmic volume control. I used DB_RANGE = 40 here instead of 60 because 60 still seemed too loud/too much range but I think they default to 60.

--- a/player/src/main/java/xyz/gianlu/librespot/player/mixing/AudioSink.java
+++ b/player/src/main/java/xyz/gianlu/librespot/player/mixing/AudioSink.java
@@ -137,9 +137,22 @@ public final class AudioSink implements Runnable, Closeable {
         if (volume < 0 || volume > Player.VOLUME_MAX)
             throw new IllegalArgumentException("Invalid volume: " + volume);
 
-        float volumeNorm = ((float) volume) / Player.VOLUME_MAX;
-        if (output.setVolume(volumeNorm)) mixing.setGlobalGain(1);
-        else mixing.setGlobalGain(volumeNorm);
+        double volumeNorm;
+        if (volume == 0) { // set to avoid fp/rounding error
+            volumeNorm = 0.0;
+        } else if (volume == Player.VOLUME_MAX) { // set to avoid fp/rounding error
+            volumeNorm = 1.0;
+        } else {
+            volumeNorm = ((double) volume) / Player.VOLUME_MAX;
+            double DB_RANGE = 40.0;
+            double DB_VOLTAGE_RATIO = 20.0;
+            double db_ratio = Math.pow(10.0, DB_RANGE / DB_VOLTAGE_RATIO);
+            double ideal_factor = Math.log(db_ratio);
+            volumeNorm = Math.exp(ideal_factor * volumeNorm) / db_ratio;
+        }
+
+        if (output.setVolume((float) volumeNorm)) mixing.setGlobalGain(1);
+        else mixing.setGlobalGain((float) volumeNorm);
     }
 
     @Override

@manfreddz
Copy link

@tofublock, if I remember correctly I found some research paper saying the value should be somewhere between 3 and 4. From that I just tested some values and liked 4 the most. I added the division just because it matched well with my clients and their speakers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants