预加载 SourceDataLine 以减少延迟

发布于 2024-10-01 00:21:25 字数 7596 浏览 11 评论 0原文

我有一个用java编写的纸牌游戏。 我想每当我将鼠标悬停在卡片上时播放声音效果。但与此同时,卡片也会“弹出”。

但是,当我尝试通过 run() 方法实现它时,它会变得滞后,也就是说,卡片不会像没有声音时那样快地弹出。

因此,我创建了另一个名为 run(int Effect)reloadLine(SourceDataLine line, int Effect) 的方法。

reloadLine(line,effect)run() 类似,只是我删除了 drain()close()< /code> 放在最后,并将其移至 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();

 }
}

这个想法是为预加载 .wav 文件的类提供两个 SourceDataLine 属性。

问题是有一点滞后

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

猫九 2024-10-08 00:21:25

我没有仔细看过你的代码,因为它真的很难阅读。您应该

  • 使用“代码示例”按钮并将
  • 代码简化为示例,仅包含理解问题所需的最少代码。

然而,据我了解,您的方法比需要的更复杂。看看这里:
Java 中的更高级音频控件

它并没有解释您的所有问题,但是应该已经减少了相当多的代码。另外,这段代码应该运行得更快。因此,即使您不会使用多线程工作,您的滞后问题也可能会这样消失。

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.

佞臣 2024-10-08 00:21:25

我通过让 SoundEFfects 类扩展 Thread 来解决这个问题

,然后我创建一个新线程,在我的主 java 类中引入 SoundEffects 类,并运行它。

所以每次鼠标悬停时,它都会运行该线程。

很抱歉,如果有人花时间试图解决问题。谢谢!

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!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文