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

PulseAudio DataLine.available() gets greater than DataLine.getBufferSize()

    Details

    • Type: Bug
    • Status: Open
    • Priority: P4
    • Resolution: Unresolved
    • Affects Version/s: 8
    • Fix Version/s: tbd_major
    • Component/s: client-libs
    • Labels:
    • Subcomponent:
    • CPU:
      x86_64
    • OS:
      linux_ubuntu

      Description

      FULL PRODUCT VERSION :
      java version "1.8.0_11"
      Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
      Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Ubuntu Linux 64bit:
      Linux Samsung-NP300E 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

      EXTRA RELEVANT SYSTEM CONFIGURATION :
      Soundcard:
      HDA Intel PCH, ALC269VC Analog

      A DESCRIPTION OF THE PROBLEM :
      When using PulseAudio to playback audio data, after the first write() call DataLine.available() returns a greater value than DataLine.getBufferSize.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      javac TestPABuffer.java
      java TestPABuffer
      input '0' to choose PulseAudio for playback.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      available() should not return a greater value than getBufferSize()

      OR
      getBufferSize() should display correct buffer size
      ACTUAL -
      available() increases corresponding to LINE_BUFFER_SIZE at the top for the DataLine.open() method.
      It's almost twice as much as LINE_BUFFER_SIZE.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.io.*;
      import java.util.*;

      import javax.sound.sampled.*;

      public class TestPABuffer
      {
      private static final float SAMPLE_RATE = 44100;
      private static final int SAMPLE_SIZE = 16;
      private static final int CHANNELS = 2;
      private static final int FRAME_SIZE = SAMPLE_SIZE * CHANNELS / 8;
      private static final boolean SIGNED = true;
      private static final boolean BIG_ENDIAN = true;

      private static final int TESTFILE_SIZE = 1048576 * 64;
      private static final int RW_BUFFER_SIZE = 1024 * 16;
      private static final int BYTES_TO_READ = 1024 * 16;
      private static final int LINE_BUFFER_SIZE = 176400;

      /**
      * Test: PulseAudio buffer
      * @param args First argument:
      * - '0': Takes given values above to create a empty byte array
      * - Path to a audio file
      * @throws Exception
      */
      public static void main(String[] args) throws Exception
      {
      AudioInputStream audioStream = null;

      //Create empty byte array
      if (args[0].equals("0"))
      {
      byte byrl_testFile[] = new byte[TESTFILE_SIZE];

      ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
      byrl_testFile);

      audioStream = new AudioInputStream(byteArrayInputStream,
      new AudioFormat(SAMPLE_RATE, SAMPLE_SIZE, CHANNELS, SIGNED,
      BIG_ENDIAN), (TESTFILE_SIZE / FRAME_SIZE));
      }//Get AudioInputStream of given audio file
      else
      {
      try
      {
      audioStream = AudioSystem
      .getAudioInputStream(new File(args[0]));
      }
      catch (FileNotFoundException exception)
      {
      throw new Exception("\nError: Argument has to be '0' (test byte array) "
      + "or a path to a audio file");
      }
      }

      Mixer mixer = chooseMixer();

      AudioFormat format = audioStream.getFormat();

      DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

      if (!mixer.isLineSupported(info))
      throw new Exception("Mixer: " + mixer.getMixerInfo().getName()
      + " does not support this format: " + format);

      SourceDataLine audioLine = (SourceDataLine) mixer.getLine(info);

      audioLine.open(format, LINE_BUFFER_SIZE);

      audioLine.start();

      System.out.println("Playback started. " + audioLine);
      double fdl_startTime = System.nanoTime();

      byte byrl_read_write_Buffer[] = new byte[RW_BUFFER_SIZE];
      int inl_bytesRead = -1;
      int inl_framesize = format.getFrameSize();
      int inl_frames;

      //read bytes from stream and write to source data line
      while ((inl_bytesRead = audioStream.read(byrl_read_write_Buffer, 0, BYTES_TO_READ)) != -1)
      {
      long ill_time = System.currentTimeMillis();

      //sleep until there is enough space for read bytes in source data line's buffer
      do{
      int inl_available = audioLine.available();
      System.out.println("Buffer available: " + inl_available + " bytes");
      System.out.println("DataLine buffer size: " + audioLine.getBufferSize() + " bytes");

      if(inl_available > audioLine.getBufferSize())
      {
      throw new Exception();
      }

      if (inl_available >= inl_bytesRead)
      break;
      Thread.sleep(100);
      } while(true);

      //write a frame directly to source data line
      inl_frames = inl_bytesRead / inl_framesize;
      for(int inl_frame = 0; inl_frame < inl_frames; inl_frame++)
      {
      audioLine.write(byrl_read_write_Buffer, inl_frame * inl_framesize,
      inl_framesize);
      }

      long ill_time2 = System.currentTimeMillis();
      //Time between write() calls
      System.out.println("\nTime between write(): " + (ill_time2 - ill_time) + " ms");
      }

      System.out.println("Playback completed.");
      double fdl_endTime = System.nanoTime();
      System.out.println("Runtime: " + ((fdl_endTime - fdl_startTime) / 1000000)
      + " milliseconds");

      audioLine.drain();
      audioLine.close();
      audioStream.close();
      }//end method main

      /**
      * method chooseMixer
      * Asks to input a number corresponding to the mixer-nr. printed to the console
      *
      * @return Mixer mixer
      */
      private static Mixer chooseMixer()
      {
      Mixer.Info mixers[] = AudioSystem.getMixerInfo();
      int inl1;

      for(inl1 = 0; inl1 < mixers.length; inl1++)
      {
      System.out.println("Mixer " + inl1 + ": " + mixers[inl1]);
      }

      int inl_mixer_number = -1;
      Scanner input = new Scanner(System.in);

      while(inl_mixer_number < 0 || inl_mixer_number >= mixers.length)
      {
      try{
      System.out.print("Choose Mixer-Nr.: ");
      inl_mixer_number = input.nextInt();

      if(inl_mixer_number < 0 || inl_mixer_number >= mixers.length)
      {
      System.out.print("\nWrong input! Input number from 0 to "
      + (mixers.length - 1) + "\n\n");
      }
      }
      catch(InputMismatchException e)
      {
      System.out.print("\nWrong input! Input number from 0 to "
      + (mixers.length - 1) + "\n\n");
      }

      input = new Scanner(System.in);
      }

      input.close();

      Mixer mixer = AudioSystem.getMixer(mixers[inl_mixer_number]);

      return mixer;
      }//end method chooseMixer
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Choose a mixer instead of default PulseAudio when prompt.

        Activity

          People

          • Assignee:
            serb Sergey Bylokhov
            Reporter:
            webbuggrp Webbug Group
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated: