我可以通过操作targetDataLine生成的字节数组来更改音频内容的音量吗?我知道我不能用java改变操作系统的音量。所以我问自己是否可以通过操纵信号本身来改变音量。可能吗?如果是的话怎么样?
答案 0 :(得分:1)
如果你有样品,改变增益很简单,只需乘以所有样品:
samples[i] = samples[i] * 0.5f; // reduce by -6dB (half)
直接乘以字节,不推荐,除非您碰巧有8位PCM签名。如果您打算自己学习如何解码字节流,可能会看到我的问答'How do I use audio sample data from Java Sound'。
现在,从理论上讲,这也可以通过Control
来完成,Line#getControl
可以从MASTER_GAIN
获得;但是,我发现TargetDataLine不支持这一点。我确实发现SourceDataLine支持{{3}}所以如果你正在播放录音,你可以使用输出。
这是一个用JSlider演示的MCVE。
重要提示:如果您运行此示例,请确保使用耳机,因为它会执行实时录制的播放。如果您使用扬声器,它会反馈。为安全起见,滑块默认为0。
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import java.awt.BorderLayout;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.TargetDataLine;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.FloatControl;
public class LineGain {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("Gain");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel content = new JPanel(new BorderLayout());
JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 100, 0);
content.add(slider, BorderLayout.CENTER);
Recorder rec = null;
try {
rec = new Recorder();
rec.setGain(slider.getValue());
} catch(Exception e) {
panic(e);
}
slider.addChangeListener(new Listener(rec));
frame.setContentPane(content);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
new Thread(rec).start();
}
});
}
static class Listener implements ChangeListener {
Recorder rec;
Listener(Recorder rec) {
this.rec = rec;
}
@Override
public void stateChanged(ChangeEvent ce) {
JSlider source = (JSlider)ce.getSource();
Integer newValue = source.getValue();
rec.setGain(newValue);
}
}
static class Recorder implements Runnable {
final Object memSync = new Object();
final AudioFormat fmt = new AudioFormat(44100f, 16, 1, true, false);
final int bufferSize = 2048;
final TargetDataLine in;
final SourceDataLine out;
final FloatControl ctrl;
Recorder() throws Exception {
in = AudioSystem.getTargetDataLine(fmt);
out = AudioSystem.getSourceDataLine(fmt);
ctrl = (FloatControl)out.getControl(FloatControl.Type.MASTER_GAIN);
in.open(fmt, bufferSize);
out.open(fmt, bufferSize);
}
void setGain(int percent) {
synchronized(memSync) {
float volume = percent / 100f;
float range = Math.abs(ctrl.getMaximum() - ctrl.getMinimum());
float value = ctrl.getMinimum() + (volume * range);
ctrl.setValue(value);
}
}
@Override
public void run() {
try {
byte[] buf = new byte[bufferSize];
in.start();
out.start();
for(int b; (b = in.read(buf, 0, buf.length)) > -1;) {
synchronized(memSync) {}
out.write(buf, 0, b);
}
} catch(Exception e) {
panic(e);
}
in.close();
out.close();
}
}
static void panic(Exception e) {
e.printStackTrace(System.err);
System.exit(1);
}
}