Вопрос

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

Can these controls be used to change the system volume?

Это было полезно?

Решение

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.

Другие советы

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

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

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

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...

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top