Can I change the volume of the audio content by manipulating the Array of Bytes that is produced by targetDataLine? I know I can't change the operation system volume levels with java. So I'm asking myself if I can change the volume by manipulating the signal itself. Is it possible? If yes how?
-
I *think* it would depend entirely on the format of the audio stream your processing. You would need to know its spec, then modify the appropriate bytes in the stream. Im not familiar with very many, but I know that some audio formats have headers and others dont. Its possible you may only have to modify it there, or you may have to modify each byte sequence as they roll in. I know that you will probably get clipping as a result though, and degrade your audio quality so its probably not a good idea. – Mark W Oct 28 '14 at 21:26
1 Answers
If you have samples, changing the gain is simple, just multiply all the samples:
samples[i] = samples[i] * 0.5f; // reduce by -6dB (half)
Multiplying the bytes directly, not recommended, unless you happen to have 8-bit PCM signed. You may see my Q&A 'How do I use audio sample data from Java Sound' if you intend to learn how to decode the byte stream yourself.
Now, in theory this can also be done with a Control
which can be obtained from Line#getControl
; however, I've found TargetDataLine does not support this. I did find that SourceDataLine supports MASTER_GAIN
so if you are playing the recording back you can use the output.
Here is an MCVE demonstrating this with a JSlider.
Important: If you run this example, make sure to use headphones because it performs playback of the live recording. If you are using speakers it will feedback. The slider defaults to 0 for safety.
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);
}
}