我目前正在编写一个小游戏,并尝试实现背景音乐。我对 Java 中的视觉/音频非常陌生,在网上做了一些研究后,我发现 Clip 将是一个与我制作图形的方式兼容的选项。因此,我遵循了如何设置/使用它的教程,从而产生了这个“声音”类:
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.*;
public class Sound implements LineListener {
boolean isPlaybackCompleted;
int playMode;
public Sound(int mode) {
playMode = mode; // 0 = background music
}
@Override
public void update(LineEvent event) {
if (LineEvent.Type.START == event.getType()) {
isPlaybackCompleted = false;
System.out.println("Playback started.");
} else if (LineEvent.Type.STOP == event.getType()) {
isPlaybackCompleted = true;
System.out.println("Playback completed.");
}
}
public void playSound(String soundName) throws LineUnavailableException, IOException, UnsupportedAudioFileException {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("./Sounds/facilityIndoors.wav");
AudioInputStream audioStream = AudioSystem.getAudioInputStream(inputStream);
AudioFormat audioFormat = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
Clip audioClip = (Clip) AudioSystem.getLine(info);
audioClip.addLineListener(this);
audioClip.open(audioStream);
audioClip.start();
switch(playMode){
case 0:
if(isPlaybackCompleted == true) {
}
}
audioClip.close();
audioStream.close();
}
}
(请注意,我希望稍后实现 playSound 的“soundName”参数,以便我可以根据与当前地图名称共享且当前未使用的变量在曲目之间进行切换,我刚刚将路径硬编码为目前已经存在用于测试的文件)
我的 main 中有实例化和函数调用
Sound music = new Sound(0);
music.playSound(map.currentMap);
当我尝试调用 playSound 函数时,会抛出此错误:
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:209)
at java.desktop/javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1006)
at Sound.playSound(Sound.java:37)
at Program.main(Program.java:22)
一开始我以为我的文件路径拼写错误,但仔细检查后我的拼写似乎是正确的,如下所示: “Global Research FacilityJava8”是我的项目文件夹,我的图像引用似乎是从该级别开始的,所以也许 Clip 不是从项目文件夹开始,这可能是问题所在,但如果是的话,我不知道如何解决它。
我尝试检查文件路径是否存在拼写错误,并尝试从一开始就省略一些符号(例如,只是“Sounds/facilityIndoors.wav”或“/Sounds/facilityIndoors.wav”),但错误仍然存在。在发布此内容之前检查可能的重复项列表时,我还发现了一篇文章,讨论将所有资源文件夹放入一个“资源”文件夹中并将其添加到构建路径中,我现在已经尝试过,但无济于事,仍然抛出同样的错误。 (文件路径现在为“resources/Sounds/facilityIndoors.wav”)
我也遇到过同样的问题,但我没有找到解决方法,而是决定通过将音频文件复制到我的电脑上的某个位置来解决这个问题。
尝试使用此代码复制文件
File audio = new File("C://somewhere//audio.wav").mkdir();
Files.copy(getClass().getResourceAsStream("resources/Sounds/facilityIndoors.wav"), Paths.get("C://somewhere//audio.wav"), StandardCopyOption.REPLACE_EXISTING);
现在只需使用
audio
文件,就像这样
AudioInputStream audioStream = AudioSystem.getAudioInputStream(audio));
Clip audioClip = AudioSystem.getClip();
audioClip.open(audioStream);
获取正确的声音文件地址通常很棘手。如果您未在地址中指定起始“/”,则搜索相对于 .setResource 或 .getResourceAsStream 方法引用的类。在您发布的内容中,包含类 Sound
的文件夹中是否有子文件夹
/Sounds?如果没有,您将收到空错误。
“/”作为地址的前缀应该从项目文件夹开始搜索,但我承认我对它到底是如何工作的有点模糊。 (我通常会即时生成声音而不是参考资源,所以我不经常使用它)。例如,我认为基于 Maven 的项目使用 /src/main/resources 文件夹,而其他 Java 项目则使用 /src。但我必须测试一下才能确认。
它可能不再有任何区别,但作为旁注,我认为最好使用 .getResource(),它返回
URL
,而不是 .getResourceAsStream(),它返回 InputStream
。后者更加挑剔,例如,如果底层文件不支持 mark 或 reset 操作,AudioSystem.getAudioInputStream() 将拒绝该流。如果您使用 .getResource() 获取 URL
并将其提供给 .getAudioInputStream(),则不需要此验证。如果您比较重载,您可以在 AudioSystem.getAudioStream() 的 API 中了解这一点。使用 File
作为参数(第三个重载)通常不是一个好的选择,因为 Java 无法在 Jar 中定位文件。