7 Replies Latest reply: Nov 24, 2010 2:17 PM by captfoss RSS

    SourceDataLine.isActive() and SourceDataLine.isRunning() don't work

    815028
      Hi all,

      In the code snippet below, both SourceDataLine.isActive() and SourceDataLine.isRunning() always return true, even after the sound has already finished playing.

      I tested this code in the following platforms: Windows XP x86, Windows Vista x86, Windows 7 x86_64, Linux x86 and Linux x86_64. I only got the behavior I expected (exiting the while loop after finished playing the sound file) on Linux x86_64.
      What's wrong with this code? Am I misusing the API somehow?

      Thanks,

      Alex

      import java.io.File;
      import javax.sound.sampled.*;

      public class Test1 {

      public static void main(String[] args) throws Exception {
      // *** edit this path to a WAV file in your system ***
      File soundFile = new File("/arquivo/dados/workspace_3.6.1/test/src/campainha.wav");

      AudioInputStream audioStream = AudioSystem.getAudioInputStream(soundFile);
      AudioFormat format = audioStream.getFormat();

      SourceDataLine line = AudioSystem.getSourceDataLine(format);
      line.open(format);
      line.start();

      byte[] data = new byte[128000];
      int bytesRead;
      while((bytesRead = audioStream.read(data)) != -1) {
      line.write(data, 0, bytesRead);
      }

      while(line.isActive() && line.isRunning()) {
      System.out.println("isActive() and isRunning() are returning true.");
      Thread.sleep(1000);
      }

      System.out.println("Never gets here...");

      if(line.isRunning()) {
      line.stop();
      }
      line.close();

      audioStream.close();
      }

      }
        • 1. Re: SourceDataLine.isActive() and SourceDataLine.isRunning() don't work
          captfoss
          In the code snippet below, both SourceDataLine.isActive() and SourceDataLine.isRunning() always return true, even after the sound has already finished playing.
          isRunning
          boolean isRunning()Indicates whether the line is running. The default is false. An open line begins running when the first data is presented in response to an invocation of the start method, and continues until presentation ceases in response to a call to stop or because playback completes. 
          The "playback completes" thing would be for a Clip and not a normal line, so in this instance, the line will continue to return "isRunning" until you close it.
          boolean isActive()Indicates whether the line is engaging in active I/O (such as playback or capture). When an inactive line becomes active, it sends a START event to its listeners. Similarly, when an active line becomes inactive, it sends a STOP event. 
          Again, same deal. The line remains active until you call stop on it...

          Looks like you're just trying to detect when the line runs out of data? More than likely, you probably would want to base it on the amount of data the line says it's played.
          int bytesRead;
          long endingPosition = line.getLongFramePosition();
          
          while((bytesRead = audioStream.read(data)) != -1) {
            endingposition += line.write(data, 0, bytesRead);
          }
          
          while(line.getLongFramePosition() < endingPosition) {
          System.out.println("Line is still working");
          Thread.sleep(1000);
          }
          • 2. Re: SourceDataLine.isActive() and SourceDataLine.isRunning() don't work
            815028
            The "playback completes" thing would be for a Clip and not a normal line, so in this instance, the line will continue to return "isRunning" until you close it.
            I find the javadoc confusing... Thanks for pointing out that "playback completes" only applies for Clips, that's not obvious to me!
            boolean isActive()Indicates whether the line is engaging in active I/O (such as playback or capture).
            When an inactive line becomes active, it sends a START event to its listeners.
            Similarly, when an active line becomes inactive, it sends a STOP event. 
            Again, same deal. The line remains active until you call stop on it...
            Now, regarding the javadoc for isActive(), if the line is no longer "engaging in active I/O (such as playback)", shouldn't this method return false? I also found that the STOP event is also triggered only after the line is stopped...
            Looks like you're just trying to detect when the line runs out of data? More than likely, you probably would want to base it on the amount of data the line says it's played.
            int bytesRead;
            long endingPosition = line.getLongFramePosition();
            
            while((bytesRead = audioStream.read(data)) != -1) {
            endingposition += line.write(data, 0, bytesRead);
            }
            
            while(line.getLongFramePosition() < endingPosition) {
            System.out.println("Line is still working");
            Thread.sleep(1000);
            }
            Yes, exactly.
            In my real application code I was using line.drain(), but that was causing me some issues on Windows XP. I could not prove that line.drain() is broken with a simple snippet, but since I removed the call to it in my application code the issue I was having was fixed. So my problem turned to finding an alternate means of determining that the sound has finished playing.

            Thank you very much for suggesting the technique above. There's just a small modification to make it work, where the following line:
            endingposition += line.write(data, 0, bytesRead);
            should actually be:
            endingPosition += line.write(data, 0, bytesRead) / format.getFrameSize();
            but anyway, I get the idea.

            Your code snippet works with Sun's JVM, but not with the OpenJDK 6 JVM that comes with my Linux box, where the sound is truncated and stops playing abruptly. The only reliable way I have found so far for detecting that the sound has finished playing is sleeping for the entire sound duration in the main thread... :-(

            I made an incorrect statement in the first post above about the outcome of executing the first code snippet on Linux: it just turns out I was using Sun's JVM on Linux x86 and OpenJDK 6 on Linux x86_64. I only get the behaviour I expect when using OpenJDK 6 JVM (be it on Linux x86 or Linux x86_64), where the output is:
            isActive() and isRunning() are returning true.
            Never gets here...
            So could it be that the implementors of the OpenJDK 6 JVM interpreted the javadoc the same way I did?

            I would like to understand what the correct behavious should be in order to file bug reports to have these issues fixed in future releases of both JVMs, so that I can hopefully remove these hacks from my application code! :-)
            • 3. Re: SourceDataLine.isActive() and SourceDataLine.isRunning() don't work
              captfoss
              This, much like a lot of the stuff associated with the Sun JVM, aren't necessarily implemented in the way you'd expect them to be...and the API generally uses common phrases in a very technical way, so that unless you're fairly experienced with the API, you're going to read the same sentence in a vastly different way.

              What does it mean for a line to be active? It means it's in a started state ;-)

              What does it mean for a line to be running? It means it's both started and has had some data written to it in the past...

              The contention here is that Java isn't directly interfacing with the hardware, it's iterfacing with the OS... So when you open a line in Java, you'd expect to be able to write data to it and have that data play (almost) immediately... but there is a bit of work that needs to be done to "activate" a line. So until some timeout occurs (which is probably a very long timeout), the line will remain "active" even though it's just sitting there.

              Why? Because you'd complain louder if it didn't ;-)

              I'd imagine those functions to look something like this, btw
              public boolean isRunning() {
                return (state.equals(States.STARTED) && (totalDataWritten>0));
              }
              
              public boolean isActive() {
                return (state.equals(States.STARTED));
              }
              • 4. Re: SourceDataLine.isActive() and SourceDataLine.isRunning() don't work
                815028
                Apart from its mysterious behavior, I guess the API would be more useful to me if they gave me a method that would simply return whether the line is playing or not (I'm avoiding the words "active" or "running" here :-)), perhaps using the same implementation as your code snippet above.

                So, if Sun's JVM behavior is correct, I can deduce that OpenJDK 6 has two bugs in its Java Sound implementation. Where's the official issue tracker to report OpenJDK bugs, http://bugs.sun.com?
                • 5. Re: SourceDataLine.isActive() and SourceDataLine.isRunning() don't work
                  captfoss
                  812025 wrote:
                  Apart from its mysterious behavior, I guess the API would be more useful to me if they gave me a method that would simply return whether the line is playing or not (I'm avoiding the words "active" or "running" here :-)), perhaps using the same implementation as your code snippet above.
                  Considering they're just dumping data from their buffers into the OS's buffers, I doubt they have any way of knowing if the sound is still being played or not... It'd be a delay on their end too...
                  So, if Sun's JVM behavior is correct, I can deduce that OpenJDK 6 has two bugs in its Java Sound implementation. Where's the official issue tracker to report OpenJDK bugs, http://bugs.sun.com?
                  Eh, no idea on that one. Oracle's apparently not too fond of letting non-customers send in bug reports, so far as I can tell ;-)
                  • 6. Re: SourceDataLine.isActive() and SourceDataLine.isRunning() don't work
                    815028
                    I've just filed two bug reports for OpenJDK on bugs.sun.com; let's see if they get approved.

                    Thank you very much for helping out!