如何在Java中播放声音?

193

我想在我的程序中播放音频文件。我应该去哪里找?


1
看一下这个类:https://github.com/dberm22/DBoard/blob/master/src/com/dberm22/utils/MediaPlayer.java 你可以使用 (new Thread(new MediaPlayer(PATHTOFILE)).start(); 调用它。 - dberm22
12个回答

147

我编写了下面的代码,它可以正常工作。但是我认为它只能处理 .wav 格式。

public static synchronized void playSound(final String url) {
  new Thread(new Runnable() {
  // The wrapper thread is unnecessary, unless it blocks on the
  // Clip finishing; see comments.
    public void run() {
      try {
        Clip clip = AudioSystem.getClip();
        AudioInputStream inputStream = AudioSystem.getAudioInputStream(
          Main.class.getResourceAsStream("/path/to/sounds/" + url));
        clip.open(inputStream);
        clip.start(); 
      } catch (Exception e) {
        System.err.println(e.getMessage());
      }
    }
  }).start();
}

7
为了避免 Clip 在随机时间被关闭,需要添加一个 LineListener。请查看:https://dev59.com/9nRB5IYBdhLWcg3wl4Kc#577926 - yanchenko
3
使用公共API的解决方案加1。但是创建一个新的线程是否是不必要的(冗余的)呢? - Jataro
5
重复了吗?我把它做成一个新的线程,这样在第一个片段结束之前就可以再次播放声音。 - pek
4
我知道 clip.start() 会启动一个新的线程,所以我相当确定这是不必要的。 - Jataro
50
  1. Thread 是不必要的。
  2. 一个使用 Clip 的好例子,请参见 JavaSound info. page
  3. 如果一个方法需要一个 URL(或 File),请提供一个实际的 URL(或 File)而不是接受代表它们的 String。(只是我个人的想法。)
  4. e.printStackTrace();System.err.println(e.getMessage()); 提供更多信息,打字更少。
- Andrew Thompson
显示剩余14条评论

20

一个糟糕的例子:

import  sun.audio.*;    //import the sun.audio package
import  java.io.*;

//** add this into your application code as appropriate
// Open an input stream  to the audio file.
InputStream in = new FileInputStream(Filename);

// Create an AudioStream object from the input stream.
AudioStream as = new AudioStream(in);         

// Use the static class member "player" from class AudioPlayer to play
// clip.
AudioPlayer.player.start(as);            

// Similarly, to stop the audio.
AudioPlayer.player.stop(as); 

14
使用sun.audio时有公共API的替代方案。 - McDowell
4
太阳.*包不是专门为我们开发人员设计的吗? - The Student
@Tom - 可能是吧;我通常不会使用这种类型的代码。 - Greg Hurlman
43
这个例子来自一篇1997年的JavaWorld文章。太过时了,你不应该使用sun.*包。 - sproketboy
3
你曾经需要关闭“in”吗? - rogerdpack
7
+1不使用sun.*包。它们存在奇怪的错误,比如不能处理大于1MB的文件,如果前一个剪辑没有播放完就不能播放下一个剪辑等。 - rogerdpack

14

我不想写那么多代码来播放一个简单的音效。如果你有JavaFX包(已经包含在我的jdk 8中),这个方法可以运行。

private static void playSound(String sound){
    // cl is the ClassLoader for the current class, ie. CurrentClass.class.getClassLoader();
    URL file = cl.getResource(sound);
    final Media media = new Media(file.toString());
    final MediaPlayer mediaPlayer = new MediaPlayer(media);
    mediaPlayer.play();
}

提示:您需要初始化JavaFX。一种快速的方法是在应用程序中调用JFXPanel()的构造函数:

static{
    JFXPanel fxPanel = new JFXPanel();
}

这适用于包括mp3在内的所有音频格式。 - gumuruh

9

无论出于何种原因,由wchargin提供的最佳答案在我调用this.getClass().getResourceAsStream()时引发了空指针错误。

对我有效的是以下内容:

void playSound(String soundFile) {
    File f = new File("./" + soundFile);
    AudioInputStream audioIn = AudioSystem.getAudioInputStream(f.toURI().toURL());  
    Clip clip = AudioSystem.getClip();
    clip.open(audioIn);
    clip.start();
}

我会使用以下方式播放声音:

 playSound("sounds/effects/sheep1.wav");

在Eclipse项目的基本目录中(不在src文件夹内),sounds/effects/sheep1.wav被定位。


你好,安德鲁,你的代码对我有用,但我注意到它执行需要一点额外的时间...大约1.5秒。 - user5033850
1
如果找不到资源,getResourceAsStream() 将返回 null,或者如果 namenull 则抛出异常。如果给定的路径无效,则这不是顶级答案的错误。 - user85421
如果音频不是wav格式怎么办? - gumuruh
从文件系统加载文件和从类路径加载资源并不等同,即使在你的IDE中看起来是这样。 - Queeg

8

如果想在Java中播放声音,可以参考以下代码。

import java.io.*;
import java.net.URL;
import javax.sound.sampled.*;
import javax.swing.*;

// To play sound using Clip, the process need to be alive.
// Hence, we use a Swing application.
public class SoundClipTest extends JFrame {

   public SoundClipTest() {
      this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      this.setTitle("Test Sound Clip");
      this.setSize(300, 200);
      this.setVisible(true);

      try {
         // Open an audio input stream.
         URL url = this.getClass().getClassLoader().getResource("gameover.wav");
         AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
         // Get a sound clip resource.
         Clip clip = AudioSystem.getClip();
         // Open audio clip and load samples from the audio input stream.
         clip.open(audioIn);
         clip.start();
      } catch (UnsupportedAudioFileException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      } catch (LineUnavailableException e) {
         e.printStackTrace();
      }
   }

   public static void main(String[] args) {
      new SoundClipTest();
   }
}

3
我之前创建了一个游戏框架,适用于Android和桌面平台。其中处理声音的部分或许可以启发您需要解决的问题。
以下是相关代码:https://github.com/hamilton-lima/jaga/blob/master/jaga%20desktop/src-desktop/com/athanazio/jaga/desktop/sound/Sound.java
package com.athanazio.jaga.desktop.sound;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;

public class Sound {

    AudioInputStream in;

    AudioFormat decodedFormat;

    AudioInputStream din;

    AudioFormat baseFormat;

    SourceDataLine line;

    private boolean loop;

    private BufferedInputStream stream;

    // private ByteArrayInputStream stream;

    /**
     * recreate the stream
     * 
     */
    public void reset() {
        try {
            stream.reset();
            in = AudioSystem.getAudioInputStream(stream);
            din = AudioSystem.getAudioInputStream(decodedFormat, in);
            line = getLine(decodedFormat);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void close() {
        try {
            line.close();
            din.close();
            in.close();
        } catch (IOException e) {
        }
    }

    Sound(String filename, boolean loop) {
        this(filename);
        this.loop = loop;
    }

    Sound(String filename) {
        this.loop = false;
        try {
            InputStream raw = Object.class.getResourceAsStream(filename);
            stream = new BufferedInputStream(raw);

            // ByteArrayOutputStream out = new ByteArrayOutputStream();
            // byte[] buffer = new byte[1024];
            // int read = raw.read(buffer);
            // while( read > 0 ) {
            // out.write(buffer, 0, read);
            // read = raw.read(buffer);
            // }
            // stream = new ByteArrayInputStream(out.toByteArray());

            in = AudioSystem.getAudioInputStream(stream);
            din = null;

            if (in != null) {
                baseFormat = in.getFormat();

                decodedFormat = new AudioFormat(
                        AudioFormat.Encoding.PCM_SIGNED, baseFormat
                                .getSampleRate(), 16, baseFormat.getChannels(),
                        baseFormat.getChannels() * 2, baseFormat
                                .getSampleRate(), false);

                din = AudioSystem.getAudioInputStream(decodedFormat, in);
                line = getLine(decodedFormat);
            }
        } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
    }

    private SourceDataLine getLine(AudioFormat audioFormat)
            throws LineUnavailableException {
        SourceDataLine res = null;
        DataLine.Info info = new DataLine.Info(SourceDataLine.class,
                audioFormat);
        res = (SourceDataLine) AudioSystem.getLine(info);
        res.open(audioFormat);
        return res;
    }

    public void play() {

        try {
            boolean firstTime = true;
            while (firstTime || loop) {

                firstTime = false;
                byte[] data = new byte[4096];

                if (line != null) {

                    line.start();
                    int nBytesRead = 0;

                    while (nBytesRead != -1) {
                        nBytesRead = din.read(data, 0, data.length);
                        if (nBytesRead != -1)
                            line.write(data, 0, nBytesRead);
                    }

                    line.drain();
                    line.stop();
                    line.close();

                    reset();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

这段代码可能会在 stream.reset(); 处出错,导致出现 Resetting to invalid mark。你建议采取什么措施来解决这个问题? - driima
也许需要重新创建流,请参见https://dev59.com/yXbZa4cB1Zd3GeqPFGBI - hamilton.lima
1
我通过在初始化“raw”后使用raw.mark(raw.available()+1),然后在while循环中使用raw.reset()而不是stream.reset()来解决了这个问题。现在我的问题是当它回到重置时,会有一段间隔时间。我想实现一个像“Clip”一样的连续循环。我没有使用“Clip”,因为操作诸如MASTER_GAIN之类的控件会有大约500毫秒的明显延迟。这可能应该是我稍后要问的问题。 - driima
1
不是 do { ... } while 吗? - Andreas detests censorship

3

对我有效。简单变体。

public void makeSound(){
    File lol = new File("somesound.wav");
    

    try{
        Clip clip = AudioSystem.getClip();
        clip.open(AudioSystem.getAudioInputStream(lol));
        clip.start();
    } catch (Exception e){
        e.printStackTrace();
    }
}

2

有一种替代方法可以在小程序和应用程序中使用,即将音频文件转换为.java文件并直接在代码中使用。

我开发了一个工具,可以大大简化这个过程。它可以简化Java Sound API。

http://stephengware.com/projects/soundtoclass/


我使用了你的系统从一个wav文件创建了一个类,但是当我执行my_wave.play()时,它没有播放音频... 我需要初始化一个音频系统或者其他什么吗? - Nathan F.
如果它真的能够工作,那将会非常酷。但在运行play()时,get Audio Line失败了(没有抛出“java.lang.IllegalArgumentException: No line matching interface SourceDataLine supporting format PCM_UNSIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian is supported."这个异常)。很遗憾。 - phil294

2
我很惊讶没有人建议使用Applet。 使用 Applet。您需要将蜂鸣声音频文件提供为wav文件,但这可行。我在Ubuntu上尝试过: "最初的回答"
package javaapplication2;

import java.applet.Applet;
import java.applet.AudioClip;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

public class JavaApplication2 {

    public static void main(String[] args) throws MalformedURLException {
        File file = new File("/path/to/your/sounds/beep3.wav");
        URL url = null;
        if (file.canRead()) {url = file.toURI().toURL();}
        System.out.println(url);
        AudioClip clip = Applet.newAudioClip(url);
        clip.play();
        System.out.println("should've played by now");
    }
}
//beep3.wav was available from: http://www.pacdv.com/sounds/interface_sound_effects/beep-3.wav

2
自Java 9起,Applet已被弃用。 - Freya

1
import java.net.URL;
import java.net.MalformedURLException;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.IOException;
import java.io.File;
public class SoundClipTest{
    //plays the sound
    public static void playSound(final String path){
        try{
            final File audioFile=new File(path);
            AudioInputStream audioIn=AudioSystem.getAudioInputStream(audioFile);
            Clip clip=AudioSystem.getClip();
            clip.open(audioIn);
            clip.start();
            long duration=getDurationInSec(audioIn);
            //System.out.println(duration);
            //We need to delay it otherwise function will return
            //duration is in seconds we are converting it to milliseconds
            Thread.sleep(duration*1000);
        }catch(LineUnavailableException | UnsupportedAudioFileException | MalformedURLException | InterruptedException exception){
            exception.printStackTrace();
        }
        catch(IOException ioException){
            ioException.printStackTrace();
        }
    }
    //Gives duration in seconds for audio files
    public static long getDurationInSec(final AudioInputStream audioIn){
        final AudioFormat format=audioIn.getFormat();
        double frameRate=format.getFrameRate();
        return (long)(audioIn.getFrameLength()/frameRate);
    }
    ////////main//////
    public static void main(String $[]){
        //SoundClipTest test=new SoundClipTest();
        SoundClipTest.playSound("/home/dev/Downloads/mixkit-sad-game-over-trombone-471.wav");
    }
}

对于这段代码,我要赞美并感谢你!声音的持续时间很重要,需要等待/休眠直到它结束! - Daniele Privitera

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接