1

So, I'm working on a project for class wherein we have to have a game with background music. I'm trying to play a .wav file as background music, but since I can't use clips (too short for a music file) I have to play with the AudioStream.

In my first implementation, the game would hang until the song finished, so I threw it into its own thread to try and alleviate that. Currently, the game plays very slowly while the song plays. I'm not sure what I need to do to make this thread play nice with my animator thread, because we we're never formally taught threads. Below is my background music player class, please someone tell me what I've done wrong that makes it hog all the system resources.

public class BGMusicPlayer implements Runnable {

File file;
AudioInputStream in;
SourceDataLine line;
int frameSize;
byte[] buffer = new byte [32 * 1024]; 
Thread player;
boolean playing = false;
boolean fileNotOver = true;

public BGMusicPlayer (File inputFile){
    try{
        file = inputFile;
        in = AudioSystem.getAudioInputStream (inputFile);
        AudioFormat format = in.getFormat();

        frameSize = format.getFrameSize(); 

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

        line.open(); 

        player = new Thread (this);       
        player.start();
    }
    catch(Exception e){
        System.out.println("That is not a valid file. No music for you.");
    }
}


public void run() {
    int readPoint = 0;
    int bytesRead = 0;
    player.setPriority(Thread.MIN_PRIORITY);

        while (fileNotOver) {
            if (playing) {
                try {
                    bytesRead = in.read (buffer, 
                            readPoint, 
                            buffer.length - readPoint);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                if (bytesRead == -1) { 
                    fileNotOver = false; 
                    break;
                }

                int leftover = bytesRead % frameSize;

                // send to line
                line.write (buffer, readPoint, bytesRead-leftover);

                // save the leftover bytes
                System.arraycopy (buffer, bytesRead,
                        buffer, 0, 
                        leftover); 
                readPoint = leftover;
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

} 

public void start() {
    playing = true;
    if(!player.isAlive())
    player.start();
    line.start();
}

public void stop() {
    playing = false;
    line.stop();
}

}
Jacob Schoen
  • 14,034
  • 15
  • 82
  • 102
  • *"I can't use clips (too short for a music file)"* The `Clip` is very limited in size. [`BigClip`](http://stackoverflow.com/a/9470886/418556) will be able to handle sounds as large as the available memory. – Andrew Thompson Mar 15 '12 at 07:32

1 Answers1

1

You are pretty close, but there are a couple of unusual things that maybe are contributing to the performance problem.

First off, if you are just playing back a .wav, there shouldn't really be a need to deal with any "readpoint" but a value of 0, and there shouldn't really be a need for a "leftover" computation. When you do the write, it should simply be the same number of bytes that were read in (return value of the read() method).

I'm also unclear why you are doing the ArrayCopy. Can you lose that?

Setting the Thread to low priority, and putting a Sleep--I guess you were hoping those would slow down the audio processing to allow more of your game to process? I've never seen this done before and it is really unusual if it is truly needed. I really recommend getting rid of these as well.

I'm curious where your audio file is coming from. Your not streaming it over the web, are you?

By the way, the way you get your input from a File and place it into an InputStream very likely won't work with Java7. A lot of folks are reporting a bug with that. It turns out it is more correct and efficient to generate a URL from the File, and then get the AudioInputStream using the URL as the argument rather than the file. The error that can come up is a "Mark/Reset" error. (A search on that will show its come up a number of times here.)

Phil Freihofner
  • 7,645
  • 1
  • 20
  • 41
  • Looking at my comment, with the perspective of several month's additional experience, I'd say most of the points I made are irrelevant. Has the code example changed? I don't see any InputStream instance, yet I made a remark about InputStreams. I could have misread this originally. New observation is that maybe the Sleep on the sound thread could cause the slowdown, as it forces or encourages the JVM to devote more cycles to the audio thread to keep it playing? I don't really know. That could be wrong too. – Phil Freihofner Sep 17 '12 at 00:29