Can Java Sound be used to control the system volume?
Asked Answered
R

6

9

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?

Radiotransparent answered 13/1, 2013 at 7:47 Comment(0)
R
11

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.

Radiotransparent answered 13/1, 2013 at 7:47 Comment(8)
So what you're saying here is that it's not possible. Is this a JDK bug?Varitype
@Varitype The JSE makes no claims to controlling system volume so I imagine, no. But feel free to open a bug report with Oracle and get an answer 'direct from the manufacturer'.Radiotransparent
I tried the sample on linux jre 1.8.0_60 but it doesn't work; all mixer names have "Unsupported control type: Volume"; Could you give me a tip?Maud
@Maud "Could you give me a tip?" Be good to your mum.Radiotransparent
The platform in the question is not specified, as I can get it; I am using linux so may that be the reason volume control doesn't work?Maud
@Maud ".. I am using linux so may that be the reason volume control doesn't work?" Yep, looks like. Java Sound on Mac was always patchy, and while an audio 'loop back' was available on Windows, it did not seem to be available on Mac. Not sure with the case on Linux but always noted that Java Sound support was inconsistent across Java implementations (JREs).Radiotransparent
@AndrewThompson Ok thanks seems like the issue for linux is much deeper than I expected first :) So the Java Sound API is a bit platform dependent stuff my bad :P But is it always like that in linux or the volume control issue has started since some let say jre/linux version?Maud
@Maud "But is it always like that in linux or the volume control issue has started since some let say jre/linux version?" Not sure, sorry.Radiotransparent
M
1

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;
}
Manganese answered 18/4, 2013 at 6:48 Comment(1)
Nice one. But while that shows two more FloatControl.Type.MASTER_GAIN * lines here, none of them have any affect on system volume. * Still 0 for FloatControl.Type.VOLUMERadiotransparent
M
1

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();
}
}
Manganese answered 18/4, 2013 at 15:43 Comment(2)
this code will make system volume to 50% which governed by code :control.setValue((float) 0.5);Manganese
This does not seem to adjust the master volume for me (win 7)Varitype
T
0

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);
        }
    }
}
Temporary answered 15/5, 2013 at 17:47 Comment(1)
The above code does not change the master volume on Windows 7.Methylal
N
0

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

Nik answered 26/12, 2013 at 18:47 Comment(0)
H
0

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

Hort answered 6/2, 2015 at 11:39 Comment(2)
Given it does not use Java Sound, I cannot see how this is an answer to the question asked (which was quite specific about 'using Java Sound').Radiotransparent
Does the code changes the whole system sound value or for some specific media app in frames of audio player for example?Maud

© 2022 - 2024 — McMap. All rights reserved.