Set volume of Java Clip

。_饼干妹妹 提交于 2019-12-01 13:16:40

According to Killer Game Programming by Andrew Davison you can adjust volume this simple:

float range = gainControl.getMaximum() - gainControl.getMinimum();
float gain = (range * volume) + gainControl.getMinimum();
gainControl.setValue(gain);

volume being the desired volume in float (0.0f means no sound, 1.0f means full audio) gainControl is the FloatControl

Hope you can get it to work!

The MASTER_GAIN FloatControl value is in decibels, meaning it's a logarithmic scale, not a linear one.

While decibels is great for audio professionals, us programmers usually want a nice linear scale where 0.0 is silent, 0.5 is half volume, and 1.0 is full volume; by which I mean normal volume for the sound sample. When playing sound effects, we don't usually amplify sound clips, just attenuate them.

Decibel (dB) to Float Value Calculator explains the maths behind converting decibels to a linear scale, but here is the code:

In Fantom:

using [java] javax.sound.sampled::Clip
using [java] javax.sound.sampled::FloatControl
using [java] javax.sound.sampled::FloatControl$Type as FType

...
private Clip clip

Float volume {
    get {
        gainControl := (FloatControl) clip.getControl(FType.MASTER_GAIN)
        return 10f.pow(gainControl.getValue / 20f)
    }
    set {
        if (it < 0f || it > 1f) throw ArgErr("Invalid volume: $it")
        gainControl := (FloatControl) clip.getControl(FType.MASTER_GAIN)
        gainControl.setValue(20f * it.log10)
    }
}

And converted to Java:

import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;

...

private Clip clip

public float getVolume() {
    FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);        
    return (float) Math.pow(10f, gainControl.getValue() / 20f);
}

public void setVolume(float volume) {
    if (volume < 0f || volume > 1f)
        throw new IllegalArgumentException("Volume not valid: " + volume);
    FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);        
    gainControl.setValue(20f * (float) Math.log10(volume));
}

Note there is no need to involve clip.getMaxVolume() or clip.getMinVolume() because a volume of 1.0 corresponds to 0 Db (no change) and a volume of 0.1 corresponds to -20 Db (very, very quiet).

If you did want to amplify the sound clip, then there's nothing stopping you from passing in 2.0 to double the normal volume, a-la VLC.

I believe you're actually looking for the FloatControl.Type.VOLUME control, rather than MASTER_GAIN. Try this:

public static void setVolume(Clip clip, int level) {
    Objects.requireNonNull(clip);
    FloatControl volume = (FloatControl) clip.getControl(FloatControl.Type.VOLUME);
    if (volume != null) {
        volume.setValue(level / 100.0);     
    }
}

So after a bit of trial and error, I came up with this:

public static void play(Clip clip) {
    if (Settings.getSettings().isVolumeOn()) {
        FloatControl control = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
        int volume = Settings.getSettings().getVolume();
        float range = control.getMinimum();
        float result = range * (1 - volume / 100.0f);
        control.setValue(result);
        clip.start();
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!