-
Bug
-
Resolution: Unresolved
-
P3
-
8, 9, 10, 11
-
x86_64
-
windows_7
FULL PRODUCT VERSION :
java version "9.0.1"
Java(TM) SE Runtime Environment (build 9.0.1+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.1+11, mixed mode)
Also confirmed on 8_151
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]
A DESCRIPTION OF THE PROBLEM :
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).
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Download the sample wav file (or use one of your chooosing), save it to a location, notate that location on line 21 of the following code, 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.)
Here is one sample wav. It happens for all audio playback through a SourceDataLine:
https://ia801403.us.archive.org/27/items/onclassical-quality-wav-audio-files-of-classical-music/onclassical_demo_luisi_bach_partita_a-minor_bwv-827_1_small-version.wav
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The volume-heard changes immediately
ACTUAL -
There is approximately a 500ms delay between when the volume-change command is sent and the volume actually changes.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
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 );
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
None found
java version "9.0.1"
Java(TM) SE Runtime Environment (build 9.0.1+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.1+11, mixed mode)
Also confirmed on 8_151
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]
A DESCRIPTION OF THE PROBLEM :
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).
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Download the sample wav file (or use one of your chooosing), save it to a location, notate that location on line 21 of the following code, 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.)
Here is one sample wav. It happens for all audio playback through a SourceDataLine:
https://ia801403.us.archive.org/27/items/onclassical-quality-wav-audio-files-of-classical-music/onclassical_demo_luisi_bach_partita_a-minor_bwv-827_1_small-version.wav
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The volume-heard changes immediately
ACTUAL -
There is approximately a 500ms delay between when the volume-change command is sent and the volume actually changes.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
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 );
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
None found