Предварительная загрузка SourceDataLine для уменьшения задержки

У меня есть карточная игра на java. Я хотел бы воспроизводить звуковой эффект всякий раз, когда я наводил курсор на карту. Но при этом карта будет «выскакивать».

Однако, когда я попытался реализовать это с помощью метода run(), он стал лагать, то есть карты не появляются так быстро, как без звука.

поэтому я создал еще один метод с именами run(int effect) и reloadLine(SourceDataLine line, int effect).

reloadLine(line,effect) похож на run(), только я удалил drain() и close() в конце и переместил их в run(int effect).

Ниже приведен мой класс SoundEffects.java:

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();

 }
}

Идея состоит в том, чтобы иметь два атрибута SourceDataLine для класса, предварительно загруженного файлами .wav.

проблема в том, что есть небольшое отставание


person ali    schedule 08.11.2010    source источник


Ответы (3)


Я не смотрел ваш код внимательно, потому что его действительно трудно читать. Вам следует

  • используйте кнопку «Пример кода» и
  • уменьшите код до примера, который включает только минимальный код, необходимый для понимания проблемы.

Однако, насколько я понимаю, ваш подход сложнее, чем необходимо. Посмотрите здесь: Дополнительные элементы управления звуком в Java

Это не объясняет всех ваших проблем, но уже должно немного сократить ваш код. Также этот код должен работать намного быстрее. Таким образом, даже если вы НЕ будете работать в многопоточном режиме, ваша проблема с задержкой может исчезнуть просто так.

person AudioDroid    schedule 08.11.2010

Похоже, ваш класс дублирует функциональность встроенного Clip класс. Вместо этого рассмотрите возможность использования стандартной версии.

Учебники:

person finnw    schedule 20.05.2011

Я решил это, позволив классу SoundEFfects, расширяющему Thread

Затем я создаю новый поток, используя класс SoundEffects в моем основном классе Java, и запускаю его.

Поэтому каждый раз, когда он наводит курсор мыши, он запускает поток.

Извините, если кто-то потратил время, пытаясь понять проблему. Спасибо!

person ali    schedule 08.11.2010