Can Java Sound be used to control the system volume?

前端 未结 6 1262
旧时难觅i
旧时难觅i 2020-11-29 10:22

Java Sound offers FloatControl instances for various sound line functionality, and both a MASTER_GAIN & VOLUME control type.

Can these controls be

相关标签:
6条回答
  • 2020-11-29 10:58

    I am using VOLUME control type. This code works for me for XP and WIN 7, but not for OSX. See my example:

    import java.io.IOException;
    
    import javax.sound.sampled.AudioSystem;
    import javax.sound.sampled.FloatControl;
    import javax.sound.sampled.Line;
    import javax.sound.sampled.Mixer;
    import javax.swing.JPanel;
    import javax.swing.JSlider;
    import javax.swing.event.ChangeEvent;
    import javax.swing.event.ChangeListener;
    
    public class VolumeExample extends JPanel {
    
    /**
     * @return main sound control object
     * @throws Exception for any problem
     */
    private FloatControl getVolumeControl() throws Exception {
        try {
            Mixer.Info mixers[] = AudioSystem.getMixerInfo();
            for (Mixer.Info mixerInfo : mixers) {
                Mixer mixer = AudioSystem.getMixer(mixerInfo);
                mixer.open();
    
                //we check only target type lines, because we are looking for "SPEAKER target port"
                for (Line.Info info : mixer.getTargetLineInfo()) {
                    if (info.toString().contains("SPEAKER")) {
                        Line line = mixer.getLine(info);
                        try {
                            line.open();
                        } catch (IllegalArgumentException iae) {}
                        return (FloatControl) line.getControl(FloatControl.Type.VOLUME);
                    }
                }
            }
        } catch (Exception ex) {
            System.out.println("problem creating volume control object:"+ex);
            throw ex;
        }
        throw new Exception("unknown problem creating volume control object");
    }
    
       VolumeExample() {
            JSlider slider = new JSlider();
            add(slider);
    
            //this is for setting the value
            slider.addChangeListener(new ChangeListener() {
    
                @Override
                public void stateChanged(ChangeEvent e) {
                    JSlider src = (JSlider)e.getSource();
                    //if (src.getValueIsAdjusting()) return; //optional
                    if (src.getValue() % 5 !=0) return;
                    float value = src.getValue() / 100.0f;
                    try {
                        getVolumeControl().setValue(value);
                        //you can put a click play code here to have nice feedback when moving slider
                    } catch (Exception ex) {
                        System.out.println(ex);
                    }
                }
            });
    
            //and this is for getting the value
            try {
                slider.setValue((int) (getVolumeControl().getValue()*100.0f));
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-29 10:59

    add following line just after Line is initialized. this is required to open the line.

    boolean opened = line.isOpen() || line instanceof Clip;
    if(!opened){
        System.out.println("Line is not open, trying to open it...");
        line.open();
        opened = true;
    }
    
    0 讨论(0)
  • 2020-11-29 11:00

    try this it wont disappoint you.... we can modify upper example accordingly.

    import javax.sound.sampled.AudioSystem;
    import javax.sound.sampled.FloatControl;
    import javax.sound.sampled.Line;
    import javax.sound.sampled.LineUnavailableException;
    import javax.sound.sampled.Mixer;
    import javax.swing.BoxLayout;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JProgressBar;
    
    public class SoundMeter {
    
    JFrame j;
    
    public SoundMeter() {
        j = new JFrame("SoundMeter");
        j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        j.setLayout(new BoxLayout(j.getContentPane(), BoxLayout.Y_AXIS));
        printMixersDetails();
        j.setVisible(true);
    }
    public void printMixersDetails(){
        javax.sound.sampled.Mixer.Info[] mixers = AudioSystem.getMixerInfo();
        System.out.println("There are " + mixers.length + " mixer info objects");  
        for(int i=0;i<mixers.length;i++){
            Mixer.Info mixerInfo = mixers[i];
            System.out.println("Mixer Name:"+mixerInfo.getName());
            Mixer mixer = AudioSystem.getMixer(mixerInfo);
            Line.Info[] lineinfos = mixer.getTargetLineInfo();
            for(Line.Info lineinfo : lineinfos){
                System.out.println("line:" + lineinfo);
                try {
                    Line line = mixer.getLine(lineinfo);
                    line.open();
                    if(line.isControlSupported(FloatControl.Type.VOLUME)){
                        FloatControl control = (FloatControl) line.getControl(FloatControl.Type.VOLUME);
                        System.out.println("Volume:"+control.getValue());   
                        JProgressBar pb = new JProgressBar();
                        // if you want to set the value for the volume 0.5 will be 50%
                        // 0.0 being 0%
                        // 1.0 being 100%
                        control.setValue((float) 0.5);
                        int value = (int) (control.getValue()*100);
                        pb.setValue(value);
                        j.add(new JLabel(lineinfo.toString()));
                        j.add(pb);
                        j.pack();
                    }
                } catch (LineUnavailableException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        new SoundMeter();
    }
    }
    
    0 讨论(0)
  • 2020-11-29 11:11

    Here is a solution that works ONLY on OS X (I am running 10.10):

    import java.io.BufferedReader;
    import java.io.File;
    import java.io.InputStreamReader;
    
    public class MasterVolume
    {
        public void setMasterVolume(float value)
        {
            String command = "set volume " + value;
            try
            {
                ProcessBuilder pb = new ProcessBuilder("osascript","-e",command);
                pb.directory(new File("/usr/bin"));
                System.out.println(command);
                StringBuffer output = new StringBuffer();
                Process p = pb.start();
                p.waitFor();
    
                BufferedReader reader =
                        new BufferedReader(new InputStreamReader(p.getInputStream()));
    
                String line;
                while ((line = reader.readLine())!= null)
                {
                    output.append(line + "\n");
                }
                System.out.println(output);
            }
            catch(Exception e)
            {
                System.out.println(e);
            }
        }
    }
    

    You would call the method like this:

    MasterVolume.setMasterVolume(3.5f);
    

    Which would set the volume at 50% since the range is .1 to 7.0

    0 讨论(0)
  • 2020-11-29 11:12

    I was recently focusing the same problem. In the end I decided to write a little Program called VolumeChanger.exe in C++ and call this from java. Works great. You can call a exe from java with

    Process process = new ProcessBuilder(vcpath,"-u").start();

    wehre vcpath is the path to your exe file (could be realtive of course).

    If you are interested how I used this tool visit me on muteFritz

    If you are interested in the whole source code feel free to PM me...

    0 讨论(0)
  • 2020-11-29 11:16

    No, it cannot. Here is source adapted from an answer to Adjusting master volume on coderanch. The source iterates the available lines, checks if they have a control of the right type, and if so, puts them in a GUI attached to a JSlider

    import java.awt.*;
    import javax.swing.*;
    import javax.sound.sampled.*;
    import javax.swing.event.ChangeEvent;
    import javax.swing.event.ChangeListener;
    
    public class SoundMixer {
    
        public Component getGui() {
            JPanel gui = new JPanel(new GridLayout(0,1));
    
            Mixer.Info[] mixers = AudioSystem.getMixerInfo();
            System.out.println(
                    "There are " + mixers.length + " mixer info objects");
            for (Mixer.Info mixerInfo : mixers) {
                System.out.println("mixer name: " + mixerInfo.getName());
                Mixer mixer = AudioSystem.getMixer(mixerInfo);
                Line.Info[] lineInfos = mixer.getSourceLineInfo();
                for (Line.Info lineInfo : lineInfos) {
                    System.out.println("  Line.Info: " + lineInfo);
                    try {
                        Line line = mixer.getLine(lineInfo);
                        FloatControl volCtrl = (FloatControl)line.getControl(
                                FloatControl.Type.MASTER_GAIN);
                        VolumeSlider vs = new VolumeSlider(volCtrl);
                        gui.add( new JLabel(volCtrl.toString()) );
                        gui.add( vs.getVolume() );
                        System.out.println(
                                "    volCtrl.getValue() = " + volCtrl.getValue());
                    } catch (LineUnavailableException e) {
                        e.printStackTrace();
                    } catch (IllegalArgumentException iaEx) {
                        System.out.println("    " + iaEx);
                    }
                }
            }
    
            return gui;
        }
    
        public static void main(String[] args) {
            Runnable r = new Runnable() {
    
                @Override
                public void run() {
                    SoundMixer sm = new SoundMixer();
                    Component c = sm.getGui();
                    JOptionPane.showMessageDialog(null, c);
                }
            };
            // Swing GUIs should be created and updated on the EDT
            // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
            SwingUtilities.invokeLater(r);
        }
    }
    
    class VolumeSlider {
    
        JSlider volume;
    
        VolumeSlider(final FloatControl volumeControl) {
            volume = new JSlider(
                    (int) volumeControl.getMinimum() * 100,
                    (int) volumeControl.getMaximum() * 100,
                    (int) volumeControl.getValue() * 100);
            ChangeListener listener = new ChangeListener() {
    
                @Override
                public void stateChanged(ChangeEvent e) {
                    float val = volume.getValue() / 100f;
                    volumeControl.setValue(val);
                    System.out.println(
                            "Setting volume of " + volumeControl.toString() + 
                            " to " + val);
                }
            };
            volume.addChangeListener(listener);
        }
    
        public JSlider getVolume() {
            return volume;
        }
    }
    

    On this Windows 7 machine I get two controls, both from the "Java Sound Audio Engine". Neither has any effect on the current system volume.

    run:
    There are 4 mixer info objects
    mixer name: Primary Sound Driver
      Line.Info: interface SourceDataLine supporting 8 audio formats, and buffers of at least 32 bytes
        java.lang.IllegalArgumentException: Unsupported control type: Master Gain
      Line.Info: interface Clip supporting 8 audio formats, and buffers of at least 32 bytes
        java.lang.IllegalArgumentException: Unsupported control type: Master Gain
    mixer name: Speakers (VIA High Definition Audio)
      Line.Info: interface SourceDataLine supporting 8 audio formats, and buffers of at least 32 bytes
        java.lang.IllegalArgumentException: Unsupported control type: Master Gain
      Line.Info: interface Clip supporting 8 audio formats, and buffers of at least 32 bytes
        java.lang.IllegalArgumentException: Unsupported control type: Master Gain
    mixer name: Java Sound Audio Engine
      Line.Info: interface SourceDataLine supporting 8 audio formats
        volCtrl.getValue() = 0.0
      Line.Info: interface Clip supporting 8 audio formats, and buffers of 0 to 4194304 bytes
        volCtrl.getValue() = 0.0
    mixer name: Port Speakers (VIA High Definition A
    Setting volume of Master Gain with current value: 0.0 dB (range: -80.0 - 13.9794) to 0.0
    Setting volume of Master Gain with current value: 0.0 dB (range: -80.0 - 13.9794) to -0.41
    Setting volume of Master Gain with current value: 0.0 dB (range: -80.0 - 13.9794) to -0.68
    ...
    

    Swap FloatControl.Type.MASTER_GAIN for FloatControl.Type.VOLUME to see.. no controls.

    0 讨论(0)
提交回复
热议问题