是否可以通过操纵Java中的targetDataLine生成的字节流来改变声音的音量?

时间:2014-10-28 21:03:59

标签: java audio

我可以通过操作targetDataLine生成的字节数组来更改音频内容的音量吗?我知道我不能用java改变操作系统的音量。所以我问自己是否可以通过操纵信号本身来改变音量。可能吗?如果是的话怎么样?

1 个答案:

答案 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。

Gain Control

重要提示:如果您运行此示例,请确保使用耳机,因为它会执行实时录制的播放。如果您使用扬声器,它会反馈。为安全起见,滑块默认为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);
    }
}