-
Bug
-
Resolution: Fixed
-
P4
-
1.3.0, 1.3.1
-
beta2
-
generic, x86
-
generic, windows_2000
-
Verified
This bug appears if a SourceDataLine is opened with too large a buffer:
the stream cannot play, but no error is reported. The problem was originally
reported by sharon.huang@Eng. Note that in her test case, the bug appears
as a regression in sun.audio functionality.
-----
Hi Kara,
I have a 8bit 8kHz mulaw data in the size of 36061 bytes and I like to play
it for 500 times. I saved this mulaw data in a byte[] as following:
--------------------
magic # = 0x2E736E64
--------------------
dataLocation = 28
--------------------
dataSize = 36061*500
--------------------
dataFormat = 1
-------------------- note: total header size = 28
SamplingRate = 8000
--------------------
channelCnt = 1
--------------------
info = 0
---------------------
fill the data in
following 36061 bytes
---------------------
My solution to play 500 times is that: I implement a class named MySoundStream,
which extends from ByteArrayInputStream and read through data buffer 500 times.
In TestSound.java line 32, I called sun.audio.AudioPlayer.player.start with
MySoundStream as a parameter.
Then this program works fine on jdk1.1.x and jdk1.2.2, but not on jdk1.3.
On jdk1.3 it just read a very big chunck of data twice and no sound coming out.
So,
1) Can you see anything wrong in my program?
2) Is jdk1.3's sun.audio fully backward compatible with jdk1.2.2's?
Thanks a lot,
Sharon
Name: krC82822 Date: 04/26/2001
java version "1.3.1-rc1"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1-rc1-b21)
Java HotSpot(TM) Client VM (build 1.3.1-rc1-b21, mixed mode)
The SourceDataLine gotten from AudioSystem accepts a buffer size that is not
aligned to integral size of frames. The documentation of SourceDataLine.open
(AudioFormat, int) states that it *may* throw an IllegalArgumentException in
this case. It does not. For normal playback, it works fine, but when a
SourceDataLine.flush() is issued, all audio data written after that to the
SourceDataLine is not aligned anymore - for 16bit mono that means e.g. that a
high byte is played instead of low byte, and vice versa.
This problem is in between a bug, feature request and documentation request. In
my opinion, the best way to solve it is to throw an IllegalArgumentException
when the line is opened with unaligned buffer size. In this case it would be a
bug. The problem is that this error is very hard to track otherwise.
Run the following program to hear unaligned data in the first case, where the
buffer size is not an integral number of frames. Each time a flush is issued,
playback changes its alignment.
import javax.sound.sampled.*;
public class FlushBug extends Thread {
static long startTime;
static float SAMPLE_RATE=44100.0f;
static int SIGNAL_LENGTH_IN_FRAMES=(int) SAMPLE_RATE*2; // 2 seconds
static SourceDataLine line=null;
static boolean doRestart=false;
FlushBug() {
super();
}
public void run() {
// half the length of the sine wave
long waitTimeMs=bytes2Ms(SIGNAL_LENGTH_IN_FRAMES);
try {
for (int i=0; i<3; i++) {
synchronized(this) {
wait(waitTimeMs);
}
println("Flushing line");
line.flush();
doRestart=true;
}
} catch (Throwable t) {}
}
public static byte[] makeSignal(int lengthInFrames) {
int nAmplitude = 20000; // [0..32767]
float nSignalFrequency = 1000.0f;
byte[] buffer = new byte[lengthInFrames*2]; // 16bit
int index=0;
double factor =
((double) nSignalFrequency)/SAMPLE_RATE*2.0*Math.PI;
for (int nFrame = 0; nFrame < lengthInFrames; nFrame++) {
int nValue = (int) (Math.sin(((double)nFrame)*factor)
* nAmplitude);
buffer[index++] = (byte) (nValue & 0xFF);
buffer[index++] = (byte) ((nValue >>> 8) & 0xFF);
}
return buffer;
}
public static long bytes2Ms(long bytes) {
return (long) (bytes/SAMPLE_RATE*1000/2);
}
public static synchronized void println(String s) {
System.out.println(""+(System.currentTimeMillis()-startTime)
+": "+s);
}
public static void main(String[] args) {
AudioFormat format = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
SAMPLE_RATE, 16, 1, 2, SAMPLE_RATE, false);
DataLine.Info info = new DataLine.Info(
SourceDataLine.class, format);
byte[] buffer=makeSignal(SIGNAL_LENGTH_IN_FRAMES);
try {
for (int i=0; i<2; i++) {
System.out.println("");
Thread thread=new FlushBug();
line = (SourceDataLine)
AudioSystem.getLine(info);
int bufferSize=(int) (SAMPLE_RATE/4); // 125ms
if (i==1) {
System.out.println(
"Aligning line buffer size");
bufferSize-=bufferSize %
format.getFrameSize();
}
line.open(format, bufferSize);
System.out.println("Line buffer size in
bytes="+line.getBufferSize());
line.start();
thread.start();
startTime=System.currentTimeMillis();
int writeIndex=0;
while (writeIndex+200<buffer.length) {
if (doRestart) {
writeIndex=0;
doRestart=false;
println("Restarting from
beginning");
}
int nWritten = line.write(buffer,
writeIndex, 200);
writeIndex+=200;
}
line.close();
synchronized (thread) {
thread.notifyAll();
}
}
} catch (Throwable t) {
t.printStackTrace();
}
System.exit(0);
}
}
(Review ID: 121192)
======================================================================
###@###.### 2001-10-21
Verified in build 75 & further, it's fixed. The attached test cases pass with these builds.
the stream cannot play, but no error is reported. The problem was originally
reported by sharon.huang@Eng. Note that in her test case, the bug appears
as a regression in sun.audio functionality.
-----
Hi Kara,
I have a 8bit 8kHz mulaw data in the size of 36061 bytes and I like to play
it for 500 times. I saved this mulaw data in a byte[] as following:
--------------------
magic # = 0x2E736E64
--------------------
dataLocation = 28
--------------------
dataSize = 36061*500
--------------------
dataFormat = 1
-------------------- note: total header size = 28
SamplingRate = 8000
--------------------
channelCnt = 1
--------------------
info = 0
---------------------
fill the data in
following 36061 bytes
---------------------
My solution to play 500 times is that: I implement a class named MySoundStream,
which extends from ByteArrayInputStream and read through data buffer 500 times.
In TestSound.java line 32, I called sun.audio.AudioPlayer.player.start with
MySoundStream as a parameter.
Then this program works fine on jdk1.1.x and jdk1.2.2, but not on jdk1.3.
On jdk1.3 it just read a very big chunck of data twice and no sound coming out.
So,
1) Can you see anything wrong in my program?
2) Is jdk1.3's sun.audio fully backward compatible with jdk1.2.2's?
Thanks a lot,
Sharon
Name: krC82822 Date: 04/26/2001
java version "1.3.1-rc1"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1-rc1-b21)
Java HotSpot(TM) Client VM (build 1.3.1-rc1-b21, mixed mode)
The SourceDataLine gotten from AudioSystem accepts a buffer size that is not
aligned to integral size of frames. The documentation of SourceDataLine.open
(AudioFormat, int) states that it *may* throw an IllegalArgumentException in
this case. It does not. For normal playback, it works fine, but when a
SourceDataLine.flush() is issued, all audio data written after that to the
SourceDataLine is not aligned anymore - for 16bit mono that means e.g. that a
high byte is played instead of low byte, and vice versa.
This problem is in between a bug, feature request and documentation request. In
my opinion, the best way to solve it is to throw an IllegalArgumentException
when the line is opened with unaligned buffer size. In this case it would be a
bug. The problem is that this error is very hard to track otherwise.
Run the following program to hear unaligned data in the first case, where the
buffer size is not an integral number of frames. Each time a flush is issued,
playback changes its alignment.
import javax.sound.sampled.*;
public class FlushBug extends Thread {
static long startTime;
static float SAMPLE_RATE=44100.0f;
static int SIGNAL_LENGTH_IN_FRAMES=(int) SAMPLE_RATE*2; // 2 seconds
static SourceDataLine line=null;
static boolean doRestart=false;
FlushBug() {
super();
}
public void run() {
// half the length of the sine wave
long waitTimeMs=bytes2Ms(SIGNAL_LENGTH_IN_FRAMES);
try {
for (int i=0; i<3; i++) {
synchronized(this) {
wait(waitTimeMs);
}
println("Flushing line");
line.flush();
doRestart=true;
}
} catch (Throwable t) {}
}
public static byte[] makeSignal(int lengthInFrames) {
int nAmplitude = 20000; // [0..32767]
float nSignalFrequency = 1000.0f;
byte[] buffer = new byte[lengthInFrames*2]; // 16bit
int index=0;
double factor =
((double) nSignalFrequency)/SAMPLE_RATE*2.0*Math.PI;
for (int nFrame = 0; nFrame < lengthInFrames; nFrame++) {
int nValue = (int) (Math.sin(((double)nFrame)*factor)
* nAmplitude);
buffer[index++] = (byte) (nValue & 0xFF);
buffer[index++] = (byte) ((nValue >>> 8) & 0xFF);
}
return buffer;
}
public static long bytes2Ms(long bytes) {
return (long) (bytes/SAMPLE_RATE*1000/2);
}
public static synchronized void println(String s) {
System.out.println(""+(System.currentTimeMillis()-startTime)
+": "+s);
}
public static void main(String[] args) {
AudioFormat format = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
SAMPLE_RATE, 16, 1, 2, SAMPLE_RATE, false);
DataLine.Info info = new DataLine.Info(
SourceDataLine.class, format);
byte[] buffer=makeSignal(SIGNAL_LENGTH_IN_FRAMES);
try {
for (int i=0; i<2; i++) {
System.out.println("");
Thread thread=new FlushBug();
line = (SourceDataLine)
AudioSystem.getLine(info);
int bufferSize=(int) (SAMPLE_RATE/4); // 125ms
if (i==1) {
System.out.println(
"Aligning line buffer size");
bufferSize-=bufferSize %
format.getFrameSize();
}
line.open(format, bufferSize);
System.out.println("Line buffer size in
bytes="+line.getBufferSize());
line.start();
thread.start();
startTime=System.currentTimeMillis();
int writeIndex=0;
while (writeIndex+200<buffer.length) {
if (doRestart) {
writeIndex=0;
doRestart=false;
println("Restarting from
beginning");
}
int nWritten = line.write(buffer,
writeIndex, 200);
writeIndex+=200;
}
line.close();
synchronized (thread) {
thread.notifyAll();
}
}
} catch (Throwable t) {
t.printStackTrace();
}
System.exit(0);
}
}
(Review ID: 121192)
======================================================================
###@###.### 2001-10-21
Verified in build 75 & further, it's fixed. The attached test cases pass with these builds.
- relates to
-
JDK-4288683 Clip does not throw exception if data cannot be loaded.
-
- Resolved
-