设置Java Clip的音量

6

有没有办法在Java中设置Clip的相应音量?

我有这个方法:

public static void play(Clip clip) {
    if (Settings.getSettings().isVolumeOn()) {
        FloatControl volume = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
        volume.setValue(-1 * Settings.getSettings().getVolume());
        clip.start();
    }
}
Settings.getSettings().getVolume()方法返回一个Integer类型的数值,范围在0 - 100之间。
以下是不同音量对应的数值:

音量:

  1. 0 : 静音
  2. 40 : 使用耳机时最佳音量
  3. 60 : 最佳音量
  4. 100: 最大音量
因此,这个方法的结果就像VLC的刻度一样(但是只有一半,因为VLC的刻度是从0到200)。
通过使用volume.setValue(-10f),可以减少剪辑的分贝。
但我更喜欢使用volume.setValue(clip.getMaxVolume() * Settings.getSettings().getVolume()/100)这种方式。
其中,clip.getMaxVolume()将返回剪辑的最大音量。
4个回答

16

MASTER_GAIN FloatControl 的值以分贝为单位,意味着它是一个对数刻度,而不是线性的。

尽管分贝对于音频专业人员很有用,但我们程序员通常想要一个漂亮的线性比例尺,其中 0.0 表示无声, 0.5 表示一半音量, 1.0 表示最大音量;我指的是声音样本的正常音量。在播放音效时,我们通常不对声音剪辑进行放大,而只是减弱它们。

Decibel (dB) to Float Value Calculator 解释了将分贝转换为线性刻度背后的数学原理,但这里是代码:

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)
    }
}

并转换为 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));
}

请注意,不需要涉及clip.getMaxVolume()clip.getMinVolume(),因为使用上面的代码,音量1.0对应于0 Db(没有变化),音量0.1对应于-20 Db(非常,非常安静)。

如果您确实想要放大声音剪辑,则可以传入2.0来将正常音量加倍,类似于VLC。

作为一个厚颜无耻的宣传,想看我如何运用这些知识,请查看逃离机房 - 一个在浏览器和桌面Java应用程序中运行的迷你跳跃游戏。使用Fantom编写 - 玩得开心!


4
根据 Andrew Davison 的 Killer Game Programming,你可以通过以下简单方法调整音量:
float range = gainControl.getMaximum() - gainControl.getMinimum();
float gain = (range * volume) + gainControl.getMinimum();
gainControl.setValue(gain);

volume表示所需音量的浮点数(0.0f表示无声,1.0f表示完全音频) gainControlFloatControl

希望您能使其正常工作!


gainControl.getMinimum() 是必要的吗?我的方法不行吗?(两个版本都可以工作)。 - nick zoum
我只尝试过一个文件,所以我想问一下,你认为其他文件可能不能用我的方法来处理吗? - nick zoum
好的,我接受你的答案,如果你发现你的答案需要更新,请及时更正。 - nick zoum

2
我相信你实际上是在寻找FloatControl.Type.VOLUME控制,而不是MASTER_GAIN。请尝试以下内容:
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);     
    }
}

1

经过一番试验和错误,我想出了这个:

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();
    }
}

也许你想再看看 Steve 的答案;因为他建议分贝不是线性的,所以你的转换可能不会得到用户预期的结果,如将音量设置为0.5并不会是剪辑音量的一半。 - Ste

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接