Home Delay When Changing Audio Volume on Windows
Reply: 0

Delay When Changing Audio Volume on Windows

user5732
1#
user5732 Published in April 24, 2018, 8:12 am

When I change the master gain of a SourceDataLine in Windows, there is a small delay (~500ms) between when control.setValue ( newVolume ) is called and when the volume out of the speakers actually decreases.

This issue does not arise on GNU/Linux, only Window. (I have not tested on OSX).

Is there any way to get rid of this lag time? I have provided a simplified but complete example.

In order to make it run, download the sample wav file (or use one of your chooosing), save it to a location, notate that location on line 21, compile, and run. (Please make sure your speakers/headphones are at a reasonable level before running -- the music will start as soon as it is launched.)

Download wav sample here: Beethoven - US Archive.org

VolumeWindowsDelay.java:

import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

import javax.swing.JFrame;  
import javax.swing.JSlider;

public class VolumeWindowsDelay {

    private static final Logger LOGGER = Logger.getLogger( VolumeWindowsDelay.class.getName() );

    private static final int BUFFER_SIZE = 32000;

    private static String targetFile = "D:\\sample.wav";
    private static File soundFile;

    private static AudioInputStream audioInput;
    private static AudioFormat audioFormat;
    private static SourceDataLine audioOutput;

    public static void main ( String[] args ) throws Exception {

        Thread playerThread = new Thread ( () -> playSound ( targetFile ) );
        playerThread.setDaemon ( true );
        playerThread.start();

        JSlider volumeSlider = new JSlider();
        volumeSlider.setMinimum( 0 );
        volumeSlider.setMaximum( 100 );
        volumeSlider.setValue( 100 );
        volumeSlider.setMajorTickSpacing ( 25 );
        volumeSlider.setMinorTickSpacing ( 25 );
        volumeSlider.setSnapToTicks ( true );

        volumeSlider.addChangeListener ( e -> {
            double min = volumeSlider.getMinimum();
            double max = volumeSlider.getMaximum();
            double percent = (volumeSlider.getValue() - min) / (max - min);
            setVolumePercent( percent );
        });

        JFrame frame = new JFrame ( "Volume Example" );
        frame.setSize ( 400, 100 );
        frame.getContentPane().add ( volumeSlider );
        frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
        frame.setVisible ( true );
    }

    static void playSound ( String filename ) {

        String strFilename = filename;

        try {
            soundFile = new File( strFilename );
        } catch ( Exception e ) {
            e.printStackTrace();
            System.exit( 1 );
        }

        try {
            audioInput = AudioSystem.getAudioInputStream( soundFile );
        } catch ( Exception e ) {
            e.printStackTrace();
            System.exit( 1 );
        }

        audioFormat = audioInput.getFormat();

        DataLine.Info info = new DataLine.Info( SourceDataLine.class, audioFormat );
        try {
            audioOutput = (SourceDataLine) AudioSystem.getLine( info );
            audioOutput.open( audioFormat );
        } catch ( LineUnavailableException e ) {
            e.printStackTrace();
            System.exit( 1 );
        } catch ( Exception e ) {
            e.printStackTrace();
            System.exit( 1 );
        }

        audioOutput.start();

        int nBytesRead = 0;
        byte[] abData = new byte [ BUFFER_SIZE ];
        while ( nBytesRead != -1 ) {
            try {
                nBytesRead = audioInput.read( abData, 0, abData.length );
            } catch ( IOException e ) {
                e.printStackTrace();
            }
            if ( nBytesRead >= 0 ) {
                audioOutput.write( abData, 0, nBytesRead );
            }
        }

        audioOutput.drain();
        audioOutput.close();
    }

    public static void setVolumePercent ( double percent ) throws IllegalArgumentException {
        System.out.println ( "Percent requested: " + percent );

        if ( audioOutput == null ) {
            LOGGER.info( "Cannot set volume, audioOutput is null" );
            return;
        }
        if ( audioOutput.isControlSupported( FloatControl.Type.MASTER_GAIN ) ) {  //Type.VOLUME not supported by windows
            FloatControl masterGain = (FloatControl)audioOutput.getControl( FloatControl.Type.MASTER_GAIN );
            setVolume ( masterGain, percent );

        } else {
            LOGGER.info( "Cannot set volume, volume control is not supported by system for this audio format." );
        }
    }

    private static void setVolume ( FloatControl control, double percent ) {
        double min = control.getMinimum();
        double max = control.getMaximum();          
        double value = (max - min) * volumeCurveDB ( percent ) + min;
        control.setValue( (float)value );

    }

    private static double volumeCurveDB ( double input ) {
        if ( input <= 0 ) return 0;
        if ( input >= 1 ) return 1;

        double value = logOfBase( 50, 49 * input + 1 );

        if ( value < 0 ) value = 0;
        if ( value > 1 ) value = 1;

        return value;
    }

    public static double logOfBase ( int base, double num ) {
        return Math.log ( num ) / Math.log ( base );
    }
}
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.301837 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO