如何将InputStream转换为AudioInputStream

3

能否将InputStream转换为AudioInputStream?

我想在某些事件发生时播放小的声音文件,因此我创建了以下SoundThread:

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

public class SoundThread implements Runnable{

    private String filename;

    SoundThread(String filename) {
        this.filename = filename;
    }

    public void run() {
        try {
            InputStream in = ClassLoader.getSystemResourceAsStream("sounds/"+filename+".wav");
            Clip clip = AudioSystem.getClip();
            clip.open((AudioInputStream)in);
            clip.start();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NullPointerException e) {
            e.printStackTrace();
        } catch (LineUnavailableException e){
            e.printStackTrace();
        } 
    }
}

我使用以下方式运行它:

new Thread(new SoundThread("nameOfTheSoundFile")).start();

起初我使用sun.audio.AudioPlayer和sun.audio.AudioStream来处理它,但当我将那段代码放入eclipse时,它显示了错误。所以我尝试了

AudioInputStream in = (AudioInputStream)ClassLoader.getSystemResourceAsStream("sounds/"+filename+".wav");

如何将InputStream强制转换为AudioInputStream (eclipse没有显示任何错误),但运行时会抛出ClassCastException异常。这个问题有解决方案吗?

3个回答

7
使用AudioSystemURL直接获取AudioInputStream以访问资源。
URL url = ClassLoader.getResource("/sounds/"+filename+".wav");
AudioInputStream ais = AudioSystem.getAudioInputStream(url);
Clip clip = AudioSystem.getClip();
clip.open(ais);

参见AudioSystem.getAudioInputStream(InputStream),但这种方法更加危险。通常,Java声音需要一个可重新定位的输入流。由于某些我不太清楚的原因,Class.getResourceAsStream()变体有时会返回可重新定位的流。

1
@Stephen C 我尝试了两种方法,一种是使用上面的URL代码片段,另一种是使用AudioSystem.getAudioInputStream(ClassLoader.getSystemResourceAsStream("sounds/"+filename+".wav"))。两种方法都可以通过代码,但没有声音播放。我还尝试从cmd启动它(原始代码没有类加载器也可以工作),但是那里也没有声音。但特别是第二种方法,我不明白:它与原始代码完全相同,只是我不直接创建AudioInputStream,而是通过ClassLoader和.getAudioInputStream(InputStream)创建。 - Valentino Ru
好的-退一步尝试在Java Sound info. page 上展示的确切代码-'LoopSound'源码。 1)该代码是否适用于我网站上的原始循环WAV? 2)如果将URL更改为指向您的WAV,该代码是否有效? 3)如果无效,会产生什么输出? - Andrew Thompson
数字1)从eclipse中运行良好。数字2)也可以,但由于我们在静态方法中,无法使用'getClass().getResource("/sounds/"check.wav")',所以我不得不将其更改为URL'url = LoopSound.class.getResource("/sounds/check.wav")'; 当我现在将完全相同的代码复制到我的类SoundThread中时,声音没有播放。主要区别是在您的代码中,我们使用了“invokeLater()”方法,而在我的代码中,整个类都被扩展为可运行。这有什么影响? - Valentino Ru
这是现在可行的解决方案。非常感谢。编辑:我在这里发布了一些代码,但它看起来非常糟糕。我只是没有使用可运行的类并制作了一个空的SwingUtilities.invokeLater(),一切都很好。 - Valentino Ru

1

你不能进行强制类型转换。在Java中,对引用类型进行类型转换只有在真实对象已经是目标类型的实例时才有效。例如:

    String myString = new String("42");
    Object obj = (Object) myString;  // OK
    String mystery = (String) obj;   // OK
    String mystery2 = (Integer) obj; // FAIL

前两个成功是因为我们在第一行创建的字符串对象是Object的一个实例(因为StringObject的子类型),也是String的一个实例。第三个失败是因为String不是Integer
在你的例子中,从getSystemResourceAsStream获取的对象是一个包含(可能是)音频数据的原始流。它不是音频流;即不是AudioInputStream的实例。
你需要包装原始输入流,类似于这样:
    InputStream in = ClassLoader.getSystemResourceAsStream(
        "sounds/"+filename+".wav");
    AudioFormat format = ...
    int length = ...
    AudioInputStream audio = new AudioInputStream(in, format, length);

或者使用AudioSystem.getAudioInputStream(...)工厂方法之一,它会在后台进行包装。

有关正确操作的详细信息,请参见安德鲁·汤姆森的答案。


@Andrew Thompson,我在发布评论后看到您对帖子进行了编辑。我想尝试带有AudioFormat和长度的编辑解决方案,但是即使阅读API,我仍然不太清楚在哪里找到创建AudioFormat和长度的参数。我认为(因为我们尚未创建“声音文件”),没有可用的.getSampleBitRate()方法,我是对的吗?我如何获取正确的参数? - Valentino Ru
@ValentinoRu - 我>>建议<<你按照Andrew Thomson的方式来做。如果你想显式地使用构造函数,可以查看getAudioInputStream方法的代码,以了解如何形成所需的参数。 - Stephen C
1
@ValentinoRu - 我的回答的真正重点是解释Java中的强制类型转换是如何工作的,以及(因此)为什么你不能通过强制类型转换来解决这个问题。 - Stephen C

0

getSystemResourceAsStream 返回的 InputStream 不是 AudioInputStream,所以强制转换永远不会起作用。只需创建一个新的 AudioInputStream 即可。


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