开发者

Preloading the SourceDataLine to reduce lag

I have a card game in java. I would like to play a sound effect whenever i mouseOver the card. But at the same time, the card will be "popping up".

However, when i tried to implement it by the run() method, it gets laggy, that is to say, the cards do not pop up as quickly as it would without the sound.

thus i created another method called run(int effect) and reloadLine(SourceDataLine line, int effect).

reloadLine(line,effect) is similar to run(), just that i removed the drain() and close() at the end , and moved it to run(int effect).

Below is my SoundEffects.java class:

package nusMonopolyDealGUI;

import javax.media.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Scanner;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.swing.JOptionPane;

public class SoundEffects implements Runnable
{
 private static final int EXTERNAL_BUFFER_SIZE = 128000;

 private static int BUTTON_CLICK = 0;
 private final int MOUSE_OVER_CARD = 1;
 private final int MOUSE_CLICK_CARD = 2;

 private static int activeSoundEffect;

 private static SourceDataLine lineOverCard = null;
 private static SourceDataLine lineClickCard = null;

 private static ArrayList<SourceDataLine> sound;
 private static ArrayList<String> soundEffects;

 // CONSTRUCTOR //
 public SoundEffects(){
  soundEffects = new ArrayList<String>();
  populateSoundEffects();
 }

 private void populateSoundEffects() {

  try{
   Scanner scanner = new Scanner(new File("开发者_高级运维soundEffectsList.txt"));
   while(scanner.hasNextLine()){
    String line = scanner.nextLine();
    soundEffects.add(line);
   }
   scanner.close();
  }
  catch (IOException exp){
   System.out.println("soundList.txt not found!");
  }
  //update soundEffects ArrayList with paths names of type D:\the\directory\path\... 
  for (int i = 0; i <soundEffects.size(); i ++){
   String path = soundEffects.get(i);
   URL pathURL = getClass().getResource("/music/" + path + ".wav");
   String pathString = pathURL.toString();
   String properPathString = pathString.replace("file:/", "");
   soundEffects.set(i, properPathString);

  }
  //fill up the class attribute lines first for fast playback
  reloadLine(lineOverCard, MOUSE_OVER_CARD);
  reloadLine(lineClickCard, MOUSE_CLICK_CARD);
 }

 // METHODS //

 public void setActiveSound(int i){
  activeSoundEffect = i;
 }

 public void run(int effect){

  switch(effect){

  case MOUSE_OVER_CARD:
   System.out.println("lineopen: "+ lineOverCard.isOpen());
   if (!lineOverCard.isActive()){
   lineOverCard.drain();
   lineOverCard.close();
   }
   reloadLine(lineOverCard, MOUSE_OVER_CARD);
   break;

  case MOUSE_CLICK_CARD:
   lineClickCard.drain();
   lineClickCard.close();
   reloadLine(lineClickCard, MOUSE_CLICK_CARD);
   break;
  }
 }

 //reload the line to reduce waiting time to load the line from buffer.
 public void reloadLine(SourceDataLine line, int effect){

  /*
   * create an abstract object File to represent the directory of the .wav file.
   */
  String filename = soundEffects.get(effect);
  System.out.println("first time here");
  File soundFile = new File(filename);
  System.out.println(filename);

  /* create an AudioInputStream and give it the .wav file
   * @exception: dump the stack trace and exit the system.
   */

  AudioInputStream audioInputStream = null;
  try
  {
   audioInputStream = AudioSystem.getAudioInputStream(soundFile);
  }
  catch (Exception e)
  {
   e.printStackTrace();
   System.exit(1);
  }

  /*
   * get info on the .wav file
   * this info is used by Java Sound to get a compatible Line
   */
  AudioFormat audioFormat = audioInputStream.getFormat();

  /*
   * Create a SourceDataLine (used to generally play an audio file)
   * Create an DataLine.Info object to be passed into the SourceDataLine 
   * so it will fetch the compatible line (getLine(info)) to use.
  */
  //line = null;
  DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);

  try
  {
   line = (SourceDataLine) AudioSystem.getLine(info);
   line.open(audioFormat); //need to open a line before inputting audio input
  }
  catch (LineUnavailableException e)
  {
   e.printStackTrace();
   System.exit(1);
  }
  catch (Exception e)
  {
   e.printStackTrace();
   System.exit(1);
  }

  line.start();

  /*
   * Line is ready to pass audio input.
   * We write the audio data (.wav) into the line
   * 1) read data from audioInputStream into a BUFFER
   * 2) write from BUFFER to Line
   * 3) we loop 
   *    audioInputStream ---> BUFFER ---> Line
   *    until we reeach the end of audioInputStream
   *    indicated by a -1 from the read method of the audioInputStream (ie. audioInputStream.read(arg0, arg1, arg2))
  */
  int nBytesRead = 0;
  byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];

  while (nBytesRead != -1)
  {
   try
   {
    nBytesRead = audioInputStream.read(abData, 0, abData.length);
   }
   catch (IOException e)
   {
    e.printStackTrace();
   }
   if (nBytesRead >= 0)
   {
    int nBytesWritten = line.write(abData, 0, nBytesRead);
   }
  }
 }


 public void run()
 {

  /*
   * create an abstract object File to represent the directory of the .wav file.
   */
  String filename = soundEffects.get(activeSoundEffect);
  File soundFile = new File(filename);


  /* create an AudioInputStream and give it the .wav file
   * @exception: dump the stack trace and exit the system.
   */

  AudioInputStream audioInputStream = null;
  try
  {
   audioInputStream = AudioSystem.getAudioInputStream(soundFile);
  }
  catch (Exception e)
  {
   e.printStackTrace();
   System.exit(1);
  }

  /*
   * get info on the .wav file
   * this info is used by Java Sound to get a compatible Line
   */
  AudioFormat audioFormat = audioInputStream.getFormat();

  /*
   * Create a SourceDataLine (used to generally play an audio file)
   * Create an DataLine.Info object to be passed into the SourceDataLine 
   * so it will fetch the compatible line (getLine(info)) to use.
  */
  SourceDataLine line = null;
  DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);

  try
  {
   line = (SourceDataLine) AudioSystem.getLine(info);
   line.open(audioFormat); //need to open a line before inputting audio input
  }
  catch (LineUnavailableException e)
  {
   e.printStackTrace();
   System.exit(1);
  }
  catch (Exception e)
  {
   e.printStackTrace();
   System.exit(1);
  }

  line.start();

  /*
   * Line is ready to pass audio input.
   * We write the audio data (.wav) into the line
   * 1) read data from audioInputStream into a BUFFER
   * 2) write from BUFFER to Line
   * 3) we loop 
   *    audioInputStream ---> BUFFER ---> Line
   *    until we reeach the end of audioInputStream
   *    indicated by a -1 from the read method of the audioInputStream (ie. audioInputStream.read(arg0, arg1, arg2))
  */
  int nBytesRead = 0;
  byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];

  while (nBytesRead != -1)
  {
   try
   {
    nBytesRead = audioInputStream.read(abData, 0, abData.length);
   }
   catch (IOException e)
   {
    e.printStackTrace();
   }
   if (nBytesRead >= 0)
   {
    int nBytesWritten = line.write(abData, 0, nBytesRead);
   }
  }

  /*
   * after filling the line, we drain it
   * ie. play the data in the line
  */
  line.drain();

  //close the line after playing.
  line.close();

 }
}

The idea is to have two SourceDataLine attributes to the class preloaded with the .wav files.

the problem is that there is a slight lag


I have not looked at your code thoroughly, because it's really hard to read. You should

  • use the "Code Sample" button and
  • reduce the code to an example, that only includes the minimum code needed to understand the problem.

However from what I understand your approach is more complicated than needed. Have a look here: More Advanced Audio Controls in Java

It doesn't explain all your issues, but should already reduce your code by quite a bit. Also this code should work much faster. So even if you would NOT work multi-threaded, your lag issue might be gone just like that.


Your class appears to be duplicating the functionality of the built-in Clip class. Consider using the standard version instead.

Tutorials:

  • http://www.java2s.com/Code/Java/Development-Class/AnexampleofloadingandplayingasoundusingaClip.htm
  • http://download.oracle.com/javase/tutorial/sound/playing.html


I solved it by letting the SoundEFfects class extending Thread

Then i create a new Thread taking in the SoundEffects class in my main java class, and run it.

So everytime it mouse overs, it will run the thread.

Sorry if anyone spent time trying to figure the problem out. Thanks!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜