This discussion is archived
7 Replies Latest reply: Jul 15, 2011 8:32 PM by 840504 RSS

Sound leak when using unsigned

840504 Newbie
Currently Being Moderated
I've generated a simple test case which should make this pretty straightforward. I'm using Linux Ubuntu 11.04 with pulseaudio, if it makes any difference.

Basically, I generate a very simple tonal sound for the musical note A3 using Java's java.sound.sampled APIs, and Clip to open and play it.
That's all well and good, but to avoid leaking sound resources, I have to listen to the Clip (or Line) for a STOP event, and then close it. That's fine too. If I play two sounds at the same time and do this, it works fine, too...
unless the sound encoding is unsigned. For some reason, switching it from signed to unsigned (and translating the tonal wave appropriately) makes me only able to close one of the Lines, and the other one goes rogue.

Here's my code:
http://pastebin.com/1mxBJ61t
or
import java.util.Timer;
import java.util.TimerTask;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineEvent.Type;

public class Test
     {
     public static void main(String[] args)
          {
          int volume = 64; //0-256
          double seconds = 2.0;
          double tone = 220.0; //Musical note A3 = 220.0

          final int sps = 80000; //sample rate (samples per second)
          final boolean signed = false;
          final byte[] toneData = makeTone(sps,tone,(int) (sps * seconds),volume,signed);

          //play the first tone
          try
               {
               play(toneData,sps,signed);
               }
          catch (Exception e)
               {
               e.printStackTrace();
               }

          //halfway through, play the second tone
          new Timer().schedule(new TimerTask()
               {
                    @Override
                    public void run()
                         {
                         try
                              {
                              play(toneData,sps,signed);
                              }
                         catch (Exception e)
                              {
                              e.printStackTrace();
                              }
                         }
               },(long) (seconds * 500));

          //since the program doesn't know to terminate itself, we'll give it an explicit terminate
          new Timer().schedule(new TimerTask()
               {
                    @Override
                    public void run()
                         {
                         System.out.println("Terminating");
                         System.exit(0);
                         }
               },(long) (seconds * 2000));
          }

     //generates a simple tonal wave
     public static byte[] makeTone(int sps, double freq, int duration, int volume, boolean signed)
          {
          byte[] r = new byte[duration];
          for (int x = 0; x < duration; x++)
               {
               double wave = Math.sin((double) x / (sps / freq) * 2 * Math.PI);
               //taper off the end so it doesn't cut prematurely and 'blip'
               double taper = duration - x < 100 ? (duration - x) / 100. : 1;
               byte b = (byte) ((volume * wave * taper) + (signed ? 0 : 128));
               r[x] = b;
               }
          return r;
          }

     public static void play(byte[] data, int sps, boolean signed) throws Exception
          {
          AudioFormat afmt = new AudioFormat(sps,8,1,signed,false);
          DataLine.Info info = new DataLine.Info(Clip.class,afmt);
          Clip c = (Clip) AudioSystem.getLine(info);
          c.open(afmt,data,0,data.length);
          c.start();
          //listen for the clip to stop, and then close it
          c.addLineListener(new LineListener()
               {
                    @Override
                    public void update(LineEvent event)
                         {
                         System.out.println("Sound event: " + event.getType());
                         if (event.getType() == Type.STOP) event.getLine().close();
                         }
               });
          }
     }
A few modifications you can make to see what I'm talking about:
Line 21, change signed to true, and everything works great.
Line 92, assuming signed is false, comment out this line, where I close the stream, and re-run. You will observe 2 Stop events. Enable it again and you will observe only 1 Stop event.



Am I doing something wrong, or is Java's sound system just that deplorable?

Thanks in advance,
-IsmAvatar
  • 1. Re: Sound leak when using unsigned
    sabre150 Expert
    Currently Being Moderated
    I'm also using Ubuntu 11.04 with pulse audio but I get what I think is a different problem. Regardless of whether 'signed' is true or false I get the exception
    javax.sound.sampled.LineUnavailableException: line with format PCM_SIGNED 80000.0 Hz, 8 bit, mono, 1 bytes/frame,  not supported.
         at com.sun.media.sound.DirectAudioDevice$DirectDL.implOpen(DirectAudioDevice.java:494)
         at com.sun.media.sound.DirectAudioDevice$DirectClip.implOpen(DirectAudioDevice.java:1280)
         at com.sun.media.sound.AbstractDataLine.open(AbstractDataLine.java:107)
         at com.sun.media.sound.DirectAudioDevice$DirectClip.open(DirectAudioDevice.java:1061)
         at com.sun.media.sound.DirectAudioDevice$DirectClip.open(DirectAudioDevice.java:1028)
         at me.grm.research.scratch.y2011.Test.play(Test.java:84)
         at me.grm.research.scratch.y2011.Test$1.run(Test.java:43)
         at java.util.TimerThread.mainLoop(Timer.java:512)
         at java.util.TimerThread.run(Timer.java:462)
    Sound event: Stop
    Sound event: Close
    Terminating
    where line 84 is
              c.open(afmt,data,0,data.length);
    Using unsigned is the same except for the exception line -
    javax.sound.sampled.LineUnavailableException: line with format PCM_UNSIGNED 80000.0 Hz, 8 bit, mono, 1 bytes/frame,  not supported.
    If I make the second invocation start after the first finishes then I don't get the exception. Again, regardless of whether 'signed' is true or false.

    P.S. Why a sample rate of 80,000KHz? I still get an exception making it much lower but 80,000 is way over the top.

    Sorry I can't duplicate your problem. If I get time tomorrow I will try to find a way round your problem that does not require one to get the line more than once but at the moment I can't really understand what your requirement is.

    Edited by: sabre150 on 13-Jul-2011 14:47
  • 2. Re: Sound leak when using unsigned
    840504 Newbie
    Currently Being Moderated
    I dropped it down to 8000, but it still does the same thing for me. I have noticed varying behaviors.

    I'm fairly new to this sound stuff, and at this point I'm just trying to get it to work. I have a .wav file that uses PCI Unsigned 8000 Hz that was exhibiting this problem. Ultimately it leads to the application hanging entirely, and I have to forcibly quit the JVM. Being able to close all sounds completely curtails that "feature", but it's apparently not an option for unsigned encodings.

    Plus, your exception doesn't sound that desirable either.

    Really, all I'm looking for is just some suggestions on how sound is supposed to work on Java, if it's supposed to work, because following the java tutorial/trail, a few examples, some trial and error, and backtracing the sun restricted routines to see how they do it, basically led me to this method, and it still only works half the time.

    Thanks for taking a look/trying it out, though.
    -IsmAvatar
  • 3. Re: Sound leak when using unsigned
    DarrylBurke Guru Moderator
    Currently Being Moderated
    Moderator action: Moved from Java Programming.

    db
  • 4. Re: Sound leak when using unsigned
    sabre150 Expert
    Currently Being Moderated
    IsmAvatar wrote:
    I dropped it down to 8000, but it still does the same thing for me. I
    I didn't say it would - just that 80,000 seemed a very high sample rate. Unless you are playing sound to bats using hardware much better than I am then anything higher than a sample rate of 44 KHz is a waste of time. At my age the highest frequency I can hear is about 8 KHz so any sample rate much above 16 KHz is too high.
    have noticed varying behaviors.

    I'm fairly new to this sound stuff, and at this point I'm just trying to get it to work.
    Get what to work? You have not really described, or a least I have not understood, exactly what you are trying to do. You have a clip of sound you are trying to play more than once but you are trying to "avoid leaking sound resources". Can you explain what you mean by this and why you think it necessary; a reference to some Java sound documentation would maybe help us understand.
    I have a .wav file that uses PCI Unsigned 8000 Hz that was exhibiting this problem.
    I don't think the '.wav' file was not exhibiting the problem I think your code is/was .
    Ultimately it leads to the application hanging entirely,
    I would be interested in seeing an SSCCE ( http://pscode.org/sscce.html ) that exhibits this since I have never met it.
    and I have to forcibly quit the JVM. Being able to close all sounds completely curtails that "feature", but it's apparently not an option for unsigned encodings.
    Since the difference between the two is just a toggle of one bit I don't really believe that this is a signed/unsigned problem. I believe there is something more fundamental to explain the problem.

    >
    Plus, your exception doesn't sound that desirable either.
    I didn't say it was desirable - I was just saying what I observed. My exception seems logical since the line I was trying to open was already open so a LineUnavailableException makes sense.

    >
    Really, all I'm looking for is just some suggestions on how sound is supposed to work on Java, if it's supposed to work, because following the java tutorial/trail, a few examples, some trial and error, and backtracing the sun restricted routines to see how they do it, basically led me to this method, and it still only works half the time.

    Thanks for taking a look/trying it out, though.
    -IsmAvatar
  • 5. Re: Sound leak when using unsigned
    840504 Newbie
    Currently Being Moderated
    Thanks for replying again.
    sabre150 wrote:
    IsmAvatar wrote:
    I dropped it down to 8000, but it still does the same thing for me. I
    I didn't say it would - just that 80,000 seemed a very high sample rate. Unless you are playing sound to bats using hardware much better than I am then anything higher than a sample rate of 44 KHz is a waste of time. At my age the highest frequency I can hear is about 8 KHz so any sample rate much above 16 KHz is too high.
    Makes sense.
    have noticed varying behaviors.

    I'm fairly new to this sound stuff, and at this point I'm just trying to get it to work.
    Get what to work? You have not really described, or a least I have not understood, exactly what you are trying to do. You have a clip of sound you are trying to play more than once but you are trying to "avoid leaking sound resources". Can you explain what you mean by this and why you think it necessary; a reference to some Java sound documentation would maybe help us understand.
    Well, here's the sound documentation that I'm basing my code off of: http://download.oracle.com/javase/1.5.0/docs/guide/sound/programmer_guide/chapter4.html (and chapter 3)
    Basically, my program has a play button that will play a sound that the user has loaded in (complex encodings or formats are not a concern currently). I need to be able to figure out how to handle the user clicking on the button multiple times. I would at least expect an average system to be able to play the sound twice concurrently (overlapping). Maybe I just have the wrong concept when I talk about "sound resources", but it seems like I run into problems down the road, like on my computer, after I've played the sound 30 times, it starts erroring, or if I try to play the sound twice, and then once more after they're done, it causes the application to hang. To me, this seems like it's looking for available lines, and just plum running out, or choosing a line it thinks is available, and hitting a wall. Idunno.
    I have a .wav file that uses PCI Unsigned 8000 Hz that was exhibiting this problem.
    I don't think the '.wav' file was not exhibiting the problem I think your code is/was .
    Fair enough. It's easier to fix the code then to expect to have certain types of wavs.
    Ultimately it leads to the application hanging entirely,
    I would be interested in seeing an SSCCE ( http://pscode.org/sscce.html ) that exhibits this since I have never met it.
    I think it would be wise to deal with one problem at a time for now. The program that exhibits it is large and would be time-consuming to simplify. I figure the problems are interrelated, so if we can fix the current problem, the apparent freezing issue should resolve itself as well. If not, then I can make an SSCCE for that.
    and I have to forcibly quit the JVM. Being able to close all sounds completely curtails that "feature", but it's apparently not an option for unsigned encodings.
    Since the difference between the two is just a toggle of one bit I don't really believe that this is a signed/unsigned problem. I believe there is something more fundamental to explain the problem.
    I expected as much. It's always nice to be able to fix one's own code/thinking than to hit a roadblock with the limitations of an API.

    >>
    Plus, your exception doesn't sound that desirable either.
    I didn't say it was desirable - I was just saying what I observed. My exception seems logical since the line I was trying to open was already open so a LineUnavailableException makes sense.
    I suppose so, yes. And I'm guessing your system is more than capable of playing two lines concurrently, if done correctly.
    I suppose the problem that you're seeing (and probably mine, too) is that it's trying to recycle the Line (just a guess - I could be completely off). For whatever reason, mine lets me play the recycled line twice, but yours doesn't. Both ways cause issues.
    I suppose this tells me that if I want to play another sound concurrently, I need another line, not a recycled one. Does that seem about right? If so, how might I go about doing that?
    If not, perhaps I'm a little thick and need some hints.

    In short, the problem is I would like to be able to play two sounds concurrently (seems like a reasonable enough request), but either I can't using my current code code (as you've seen), or it causes other issues (my side). So I'd like to know how to fix the code to enable this.

    Greatly appreciated.
    -IsmAvatar
  • 6. Re: Sound leak when using unsigned
    840504 Newbie
    Currently Being Moderated
    Following up on why exactly not being able to close these rogue sounds is an issue, and to confirm that it is indeed some sort of leak, the following image should quell any doubts:

    http://dl.dropbox.com/u/9975312/ossxmix.png

    -IsmAvatar
  • 7. Re: Sound leak when using unsigned
    840504 Newbie
    Currently Being Moderated
    I found a solution to my sound leak issue, by following this example:
    http://www.oracle.com/technetwork/java/index-139508.html

    For those of you interested, instead of registering a listener for the sound to stop, instead thread the sound, periodically checking isActive() until it returns false. After that, simply stop and close the sound.

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points