如何使用Java播放.wav文件

58

我正在尝试使用Java播放*.wav文件,希望做到以下几点:
当按下按钮时,播放短蜂鸣声。

我已经在谷歌上搜索了,但大部分代码都无法正常工作。有没有人能给我一个简单的代码片段来播放.wav文件?

10个回答

46

最终我成功地完成了以下操作,并且它运行得很好。

import java.io.File;
import java.io.IOException;

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;

public class MakeSound {

    private final int BUFFER_SIZE = 128000;
    private File soundFile;
    private AudioInputStream audioStream;
    private AudioFormat audioFormat;
    private SourceDataLine sourceLine;

    /**
     * @param filename the name of the file that is going to be played
     */
    public void playSound(String filename){

        String strFilename = filename;

        try {
            soundFile = new File(strFilename);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }

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

        audioFormat = audioStream.getFormat();

        DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
        try {
            sourceLine = (SourceDataLine) AudioSystem.getLine(info);
            sourceLine.open(audioFormat);
        } catch (LineUnavailableException e) {
            e.printStackTrace();
            System.exit(1);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }

        sourceLine.start();

        int nBytesRead = 0;
        byte[] abData = new byte[BUFFER_SIZE];
        while (nBytesRead != -1) {
            try {
                nBytesRead = audioStream.read(abData, 0, abData.length);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (nBytesRead >= 0) {
                @SuppressWarnings("unused")
                int nBytesWritten = sourceLine.write(abData, 0, nBytesRead);
            }
        }

        sourceLine.drain();
        sourceLine.close();
    }
}

12
这个不适用于我。我推荐一个非常简单的:http://alvinalexander.com/java/java-audio-example-java-au-play-sound。 - duleshi
11
如果这就是在Java中播放wav声音所需的步骤,那我将停止使用Java。不过,幸运的是,我怀疑这并不正确。 - Bjorn
如果你在游戏循环中尝试这样做,游戏会冻结。 - florentin nica
你的代码只能运行一次。当你尝试第二次执行代码时,文件不会播放,因为第一次执行时的AudioInputStream从未关闭。 - Gilbert Le Blanc
@Gilbert Le Blanc,那是错误的。不关闭文件并不会阻止其他人以后再次打开它,也不会在程序终止时使其在文件系统中神奇消失。而且:当程序终止时,操作系统会关闭它打开的所有文件... - Angel O'Sphere
这是一个很好的例子,说明了如何不使用异常。你应该只有一个try块和一个catch块,并在那个catch块中处理或不处理所有的异常。 - Angel O'Sphere

41

这是我能想到的不使用 sun.* 的最优雅形式:

import java.io.*;
import javax.sound.sampled.*;

try {
    File yourFile;
    AudioInputStream stream;
    AudioFormat format;
    DataLine.Info info;
    Clip clip;

    stream = AudioSystem.getAudioInputStream(yourFile);
    format = stream.getFormat();
    info = new DataLine.Info(Clip.class, format);
    clip = (Clip) AudioSystem.getLine(info);
    clip.open(stream);
    clip.start();
}
catch (Exception e) {
    //whatevers
}

1
使用这些示例代码时,我听不到任何音频。我有什么遗漏的吗? - Bryan
1
它应该是完整的,但是它也已经五年了,而且很丑陋。我相信有更好的方法可以做到这一点,使用优秀的Java库。 - tschwab
5
为了听到输出,您应该延迟应用程序的终止时间以等待回放完成。在 clip.start() 之后尝试加入 Thread.sleep(1000) - Metaphore
您可能需要在 clip.start() 前加上 'clip.loop(0);'(或者播放两次使用 'clip.loop(1);')。 - Angel O'Sphere

19

最短的形式(不需要安装任何随机库)?

public static void play(String filename)
{
    try
    {
        Clip clip = AudioSystem.getClip();
        clip.open(AudioSystem.getAudioInputStream(new File(filename)));
        clip.start();
    }
    catch (Exception exc)
    {
        exc.printStackTrace(System.out);
    }
}

唯一的问题是没有好的方法来使这个方法阻塞以便在*.wav完成后关闭和处理数据。

clip.drain()说它是阻塞的,但实际上不是。剪辑片段并没有立即start()之后运行。

我发现的唯一可行但丑陋的方法是:

// ...
clip.start();
while (!clip.isRunning())
    Thread.sleep(10);
while (clip.isRunning())
    Thread.sleep(10);
clip.close();

16
您可以使用事件监听器在剪辑播放后关闭它。
import java.io.File;
import javax.sound.sampled.*;

public void play(File file) 
{
    try
    {
        final Clip clip = (Clip)AudioSystem.getLine(new Line.Info(Clip.class));

        clip.addLineListener(new LineListener()
        {
            @Override
            public void update(LineEvent event)
            {
                if (event.getType() == LineEvent.Type.STOP)
                    clip.close();
            }
        });

        clip.open(AudioSystem.getAudioInputStream(file));
        clip.start();
    }
    catch (Exception exc)
    {
        exc.printStackTrace(System.out);
    }
}

请记住,如果您想等到音频流被 Line 处理后再执行操作,可以在句子开头添加 "clip.drain()"。 - Victor

4

这个代码片段可以正常工作,经过测试可用于播放Windows声音:

public static void main(String[] args) {
        AePlayWave aw = new AePlayWave( "C:\\WINDOWS\\Media\\tada.wav" );
        aw.start();     
}

如果您仍然遇到问题,请尝试更改音频设备。还可以参考此处推荐的工具:https://dev59.com/5HI95IYBdhLWcg3wvgl9#2216886 - stacker

2

一个可以播放WAV文件的类,会阻塞直到声音播放完毕:

class Sound implements Playable {

    private final Path wavPath;
    private final CyclicBarrier barrier = new CyclicBarrier(2);

    Sound(final Path wavPath) {

        this.wavPath = wavPath;
    }

    @Override
    public void play() throws LineUnavailableException, IOException, UnsupportedAudioFileException {

        try (final AudioInputStream audioIn = AudioSystem.getAudioInputStream(wavPath.toFile());
             final Clip clip = AudioSystem.getClip()) {

            listenForEndOf(clip);
            clip.open(audioIn);
            clip.start();
            waitForSoundEnd();
        }
    }

    private void listenForEndOf(final Clip clip) {

        clip.addLineListener(event -> {
            if (event.getType() == LineEvent.Type.STOP) waitOnBarrier();
        });
    }

    private void waitOnBarrier() {

        try {

            barrier.await();
        } catch (final InterruptedException ignored) {
        } catch (final BrokenBarrierException e) {

            throw new RuntimeException(e);
        }
    }

    private void waitForSoundEnd() {

        waitOnBarrier();
    }
}

1
另一种使用 AudioInputStream 实现的方法是:

import java.io.File;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.swing.JDialog;
import javax.swing.JFileChooser;

public class CoreJavaSound extends Object implements LineListener {
    File soundFile;

    JDialog playingDialog;

    Clip clip;

    public static void main(String[] args) throws Exception {
        CoreJavaSound s = new CoreJavaSound();
    }

    public CoreJavaSound() throws Exception {
        JFileChooser chooser = new JFileChooser();
        chooser.showOpenDialog(null);
        soundFile = chooser.getSelectedFile();

        System.out.println("Playing " + soundFile.getName());

        Line.Info linfo = new Line.Info(Clip.class);
        Line line = AudioSystem.getLine(linfo);
        clip = (Clip) line;
        clip.addLineListener(this);
        AudioInputStream ais = AudioSystem.getAudioInputStream(soundFile);
        clip.open(ais);
        clip.start();
    }

    public void update(LineEvent le) {
        LineEvent.Type type = le.getType();
        if (type == LineEvent.Type.OPEN) {
            System.out.println("OPEN");
        } else if (type == LineEvent.Type.CLOSE) {
            System.out.println("CLOSE");
            System.exit(0);
        } else if (type == LineEvent.Type.START) {
            System.out.println("START");
            playingDialog.setVisible(true);
        } else if (type == LineEvent.Type.STOP) {
            System.out.println("STOP");
            playingDialog.setVisible(false);
            clip.close();
        }
    }
}

为什么要使用 extends Object - MazeOfEncryption
1
@MazeOfEncryption 我真的不记得了 :) - God

1

一种不使用Java反射的解决方案:DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat)

Java反射会降低性能。 运行: java playsound absoluteFilePathTo/file.wav

import javax.sound.sampled.*;
import java.io.*;
public class playsound {

    public static void main (String args[]) throws Exception {
        playSound (args[0]);
    }

    public static void playSound () throws Exception {
        AudioInputStream 
        audioStream = AudioSystem.getAudioInputStream(new File (filename));

        int BUFFER_SIZE = 128000;
        AudioFormat audioFormat = null;
        SourceDataLine sourceLine = null;

        audioFormat = audioStream.getFormat();

        sourceLine = AudioSystem.getSourceDataLine(audioFormat);
        sourceLine.open(audioFormat);
        sourceLine.start();

        int nBytesRead = 0;
        byte[] abData = new byte[BUFFER_SIZE];
        while (nBytesRead != -1) {
            try {
                nBytesRead = 
                audioStream.read(abData, 0, abData.length);
            } catch (IOException e) {
                e.printStackTrace();
            }

            if (nBytesRead >= 0) {
                int nBytesWritten = sourceLine.write(abData, 0, nBytesRead);
            }
        }

        sourceLine.drain();
        sourceLine.close();
    }

}

0

我采用了 @greenLizard 的代码,并使其更加健壮。

  1. 我关闭了 AudioInputStream
  2. 我使用了 BufferedInputStream。 因为 AudioSystemgetAudioInputStream 偶尔会抛出 IOException,因为 getAutoInputSytream 方法无法备份输入流并重新开始。

希望不再发现任何异常。

这是修改后的代码。 ErrorDisplayDialog 在 Java Swing 应用程序中将异常显示为 JDialog。只需用 e.printStackTrace(); 替换即可。

private void playWavFile(String fileName) {
    InputStream inputStream = getClass().getResourceAsStream(fileName);
    BufferedInputStream bufferedInputStream = new BufferedInputStream(
            inputStream);
    AudioInputStream audioStream = null;
    AudioFormat audioFormat = null;

    try {
        audioStream = AudioSystem.getAudioInputStream(bufferedInputStream);
        audioFormat = audioStream.getFormat();
    } catch (UnsupportedAudioFileException e) {
        new ErrorDisplayDialog(view.getFrame(),
                "UnsupportedAudioFileException", e);
        return;
    } catch (IOException e) {
        new ErrorDisplayDialog(view.getFrame(), "IOException", e);
        return;
    }

    DataLine.Info info = new DataLine.Info(SourceDataLine.class,
            audioFormat);
    SourceDataLine sourceLine;
    try {
        sourceLine = (SourceDataLine) AudioSystem.getLine(info);
        sourceLine.open(audioFormat);
    } catch (LineUnavailableException e) {
        new ErrorDisplayDialog(view.getFrame(), "LineUnavailableException",
                e);
        return;
    }

    sourceLine.start();

    int nBytesRead = 0;
    byte[] abData = new byte[128000];
    while (nBytesRead != -1) {
        try {
            nBytesRead = audioStream.read(abData, 0, abData.length);
        } catch (IOException e) {
            new ErrorDisplayDialog(view.getFrame(), "IOException", e);
            return;
        }

        if (nBytesRead >= 0) {
            sourceLine.write(abData, 0, nBytesRead);
        }
    }

    sourceLine.drain();
    sourceLine.close();

    try {
        audioStream.close();
    } catch (IOException e) {
        new ErrorDisplayDialog(view.getFrame(), "IOException", e);
    }
}

0

你也可以这样使用 AudioStream:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

import sun.audio.AudioPlayer;
import sun.audio.AudioStream;

public class AudioWizz extends JPanel implements ActionListener {

    private static final long serialVersionUID = 1L; //you like your cereal and the program likes their "serial"

    static AudioWizz a;
    static JButton playBuddon;
    static JFrame frame;

    public static void main(String arguments[]){

        frame= new JFrame("AudioWizz");
        frame.setSize(300,300);
        frame.setVisible(true);
        a= new AudioWizz();
        playBuddon= new JButton("PUSH ME");
        playBuddon.setBounds(10,10,80,30);
        playBuddon.addActionListener(a);

        frame.add(playBuddon);
        frame.add(a);
    }

    public void actionPerformed(ActionEvent e){ //an eventListener
        if (e.getSource() == playBuddon) {
            try {
                InputStream in = new FileInputStream("*.wav");
                AudioStream sound = new AudioStream(in);
                AudioPlayer.player.start(sound);
            } catch(FileNotFoundException e1) {
                e1.printStackTrace();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

}

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