Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4326534

SourceDataLine fails to play if buffer size is too large

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P4 P4
    • 1.4.0
    • 1.3.0, 1.3.1
    • client-libs
    • 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.

            fbomerssunw Florian Bomers (Inactive)
            kkytlesunw Kara Kytle (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: