JDK-4997635 : Win: SourceDataLine playback loops endlessly unless you manually stop()
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.sound
  • Affected Version: 5.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2004-02-20
  • Updated: 2004-04-02
  • Resolved: 2004-04-02
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
5.0 b46Fixed
Description

Name: rmT116609			Date: 02/19/2004


FULL PRODUCT VERSION :
java version "1.5.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b32c)
Java HotSpot(TM) Client VM (build 1.5.0-beta-b32c, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2000 [Version 5.00.2195]

EXTRA RELEVANT SYSTEM CONFIGURATION :
DirectX 9.0b
Analog Devices SoundMAX (smwdm.sys 5.12.0001.3045)

A DESCRIPTION OF THE PROBLEM :
When playing back an audio stream using the javax.sound.sampled SourceDataLine API, sound does not stop when the end of data has been reached. Instead, the audio loops infinitely.

For example, if you write 5000 samples of a sine wave tone to a SourceDataLine at 44100 Hz, then do not play any more, you would expect to hear a ~0.1 second tone followed by silence. In fact, you hear the 0.1 second tone, then a pause, then the same tone,  and so on.

This loop continues indefinitely unless you manually stop the line, close the buffer, etc.  The specification does not require you to stop the buffer. You should not need to keep track of precise timing for when the buffer is likely to need stopping.

(This is likely the result of the DirectX buffer not being stopped.)


It is easiest to detect this problem with a short sound, but the same problem applies even if you write a large quantity of audio data. If you change the LOOPS constant in the attached sample code, for example to 1000 (which should last approximately 2 seconds) the sinewave will now become a continuous, infinite tone lasting until the program exits.


This precise problem does not occur in 1.4.2_03. The test case works correctly in 1.4.2_03 (a bit of a miracle in itself since JavaSound streaming is still seriously broken there, but hey).


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Write a small quantity of sound data to a SourceDataLine
2. Do not call stop() on the line.
3. Sleep for a bit

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
A short sound should be heard, once.
ACTUAL -
The short sound was repeated at approximately 1 second intervals.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
No errors appear.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.ByteArrayOutputStream;

import javax.sound.sampled.*;

public class JavaSound
{
	public static void main(String[] args) throws Exception
	{
		short[] asSineWave=new short[100];
		for(int i=0;i<100;i++)
		{
			asSineWave[i]=(short)(32767.0*Math.sin(2.0 * Math.PI * (double)i / 100.0));
		}
		
		boolean bBigEndian=false;
				
		int COPIES=50;
		ByteArrayOutputStream baosConversionBuffer=new ByteArrayOutputStream();
		for(int iCopy=0;iCopy<COPIES;iCopy++)
		{
			if(bBigEndian)
			{
				for(int i=0;i<asSineWave.length;i++)
				{
					baosConversionBuffer.write(asSineWave[i]>>8);
					baosConversionBuffer.write(asSineWave[i]&0xff);
				}
			}
			else
			{
				for(int i=0;i<asSineWave.length;i++)
				{
					baosConversionBuffer.write(asSineWave[i]&0xff);
					baosConversionBuffer.write(asSineWave[i]>>8);
				}
			}
		}
				
		AudioFormat af=new AudioFormat(44100f,16,1,true,bBigEndian);
		SourceDataLine sdl=(SourceDataLine)
			AudioSystem.getLine(new DataLine.Info(SourceDataLine.class,af));
		sdl.open(af);
		sdl.start();
		sdl.write(baosConversionBuffer.toByteArray(),0,baosConversionBuffer.size());
		
		Thread.sleep(20000);
	}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
You could write a buffer's worth of extra blank data at the end of the audio (so that the looping part is blank and therefore inaudible).... while this would be a possible solution for the test case it would be somewhat difficult in real use, particularly in Internet streaming applications which need graceful handling for the case when audio data does not arrive on time.
(Incident Review ID: 239500) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger-beta2 FIXED IN: tiger-beta2 INTEGRATED IN: tiger-b46 tiger-beta2
14-06-2004

SUGGESTED FIX General options: 1) an own thread (native or Java) that monitors the line and acts on underrun situations by clearing the buffer. Problem: huge system overhead, thread resources. 2) Use DirectSound's event notification at specific buffer points during playback to monitor "health" of buffer. Problem: DirectSound only sets an Event object, would also require an own native thread. 3) Re-use Event dispatcher thread for monitoring all line's health. Problem: overloaded event dispatcher, monitoring can be blocked if event handler blocks. The best solution seems to be 3) as it minimizes resource requirements.
11-06-2004

PUBLIC COMMENTS Is fixed for tiger-beta 2 by monitoring the line in regular intervals for underruns.
10-06-2004

EVALUATION Should get fixed for tiger RR. Need to find a solution that does not harm performance. ###@###.### 2004-03-01
01-03-2004