提问人:Daniel Hardeman 提问时间:10/28/2023 最后编辑:Daniel Hardeman 更新时间:11/1/2023 访问量:70
为什么 JavaFX MediaPlayer 音频播放在 Windows 和 Mac 上完美运行,但在 Linux(popOS/Ubuntu)上出现问题?
Why is JavaFX MediaPlayer audio playback working perfectly on windows and mac, but having issues on Linux (popOS/Ubuntu)?
问:
我有一个 JavaFX 俄罗斯方块游戏,它使用 MediaPlayer 类来播放音乐和声音效果。具体来说,在处理所有音频的 SoundPlayer 类中,我在游戏启动时加载游戏的 .wav 文件,并迭代每个文件,为每个文件创建一个 Media 对象,然后为每个 Media 对象创建一个 MediaPlayer 对象。MediaPlayer(每个声音一个)存储在数组中,以便将来使用类的 playSound 方法进行播放,该方法接受一个整数参数,该参数指示与所需声音效果对应的 MediaPlayer 的索引。它在 Windows 和 Mac 上完美运行,但在我运行 popOS 的笔记本电脑上,虽然它在技术上可以工作,但它存在以下问题:
当游戏启动时,一些声音会正常播放,但玩几次后,播放音量会变得非常安静,即使 MediaPlayer 的实际音量属性没有改变,您也几乎听不到它
有些声音不会播放整个声音,而只能播放其中的一部分。
背景音乐和大多数声音都能正常工作,但对于某些人来说,会出现这些问题。 这是我在游戏触发时播放声音的代码:
public void playSound(int index)
{
MediaPlayer mediaPlayer = mediaPlayerList[index];
mediaPlayer.stop();
mediaPlayer.seek(Duration.ZERO);
mediaPlayer.play();
}
这在我的其他计算机上完美运行,但我只在我的 popOS 笔记本电脑上遇到这个问题。 有谁知道这是 Java.sound 工作方式的 Linux 问题,还是我的笔记本电脑硬件问题?我尝试配置java.sound.config文件,但无济于事,不知所措。
如上所述,我尝试查找 java.sound 配置文件的修复程序,但我似乎没有任何发现,所以我开始认为这可能是我的笔记本电脑的问题,但我想我会问看看是否有人知道我可能错过的任何事情。我目前是一名 CS 学生,这是我第一次尝试使用 Java 进行音频播放的项目,所以我对它在低层次上的工作方式相当无知。如果有人知道任何修复程序或潜在原因,将不胜感激。此外,我的音频驱动程序是最新的,我在这台笔记本电脑上尝试过的所有其他音频播放都工作正常。
最初,我使用 Java Sound API 的 Clip 类来播放声音效果,音频播放在 Clips 上可以正常工作,但我使用 Clips 的问题是,当声音被用户输入快速触发时,就像我的游戏中经常出现的情况一样,我发现音频经常跳过,这可能是由于我对多线程的了解有限, 所以我决定尝试 JavaFX 的 MediaPlayer,它的响应速度和优化性要高得多,而且正如我所说,在我的 Windows 机器和 Mac 上完美运行......它只在我的 Linux 笔记本电脑上遇到这个问题。如果有任何帮助,我正在使用 openjdk-17....我不知道这是否是 open-jdk 与 oracle jdk 的具体问题,或者类似性质的问题。
答:
开源 AudioCue 库(可通过 Maven 获得)基本上是为支持多线程而编写的。它应该适合您,并且可以免费使用。Clip
评论中有很多好点(应该是用来对原始帖子提出问题和澄清,afaik),在我看来,这些观点很可能被列为“答案”。我在这里的回答将重复其中的一些观点。
对于简短的提示,首选只加载一次并且可以直接从内存中播放多次的类:、、 ,以及需要从文件或其他形式的流式处理中读取的类:、。Clip
AudioClip
SourceDataLine
MediaPlayer
好消息是它确实支持并发播放,这与流式播放方法不同。因此,您可以采用相同的方法并多次播放它,而无需先调用方法并重新定位其播放位置。每次迭代都应该一直进行到最后。AudioClip
Clip
AudioClip
stop
曾经有一段时间,一些 Linux 操作系统只允许在给定时刻播放单个声源,但那是很久以前的事了。IDK(如果这可能是您正在测试的特定操作系统配置的问题)。如果是这样,回到我在开头提到的库,它有一个类,用于将所有声音提示汇集到单个输出行中,这将是这种情况的解决方法。AudioCue
AudioMixer
评论
我把我和 Slaw 的笔记从评论移到了答案上。它们可能无法解决实际问题,相反,它们是解决 JavaFX 媒体播放某些问题的指南。
使用 AudioClip 进行短音频播放
javafx.scene.media.AudioClip
通常更适合于短音效。
请参阅 Phil 的回答中的进一步解释以及 javadoc 中的描述:AudioClip
AudioClip 表示可以以最小延迟播放的音频片段。剪辑的加载方式与 Media 对象类似,但具有不同的行为,例如,Media 无法自行播放。AudioClip 也可以立即使用。播放行为是即发即弃:一旦调用了其中一种播放方法,唯一可操作的控件就是 stop()。AudioClip 也可以同时播放多次。
要使用 Media 完成相同的任务,必须为并行播放的每个声音创建一个新的 MediaPlayer 对象。但是,媒体对象更适合长时间播放的声音。这主要是因为 AudioClip 在内存中存储了整个声音的原始、未压缩的音频数据,对于长音频剪辑来说,这些数据可能相当大。MediaPlayer 在内存中预卷的解压缩音频数据仅够播放一小段时间,因此对于长剪辑的内存效率要高得多,尤其是在压缩它们时。
如评论中所述:
我最终使用了 AudioClips 而不是 MediaPlayer,因为它们似乎更适合创建和对象一次,然后使用相同的实例进行重复播放,而不是创建和处置 MediaPlayer。现在一切都很好。
实现 MediaPlayers 的错误处理
我没有收到任何错误
媒体包 javadoc 中有错误处理代码。尝试一下,看看它是否报告了任何内容。
如果默认情况下处理,则不会报告大多数媒体错误,它可能只是静默失败。如果要彻底报告和处理它们,则需要实现媒体播放器包文档中完整而广泛的媒体异常逻辑。
当然,即使如此,如果问题确实出在代码中,或者系统没有准确检测问题并提供问题反馈,则可能仍然没有报告实际错误。
检查代码逻辑的正确性
无法播放完整声音(音频中断)
您的代码会停止播放器。然后从头开始播放。根据呼叫的时间,预计在完成播放之前切断现有声音。
如果不使用 java.sound
API 和文档,请忽略它们
java.sound
与 JavaFX 媒体无关,配置它不会帮助您使用 JavaFX 或 API 播放声音。MediaPlayer
AudioClip
考虑异步操作
与 JavaFX 中的许多框架大多是单线程不同,当您查看 MediaPlayer 文档时,它会指出:
MediaPlayer 的操作本质上是异步的。
通常,您不需要太担心这一点,但它可能会导致您的平台上出现问题。如果玩家处于 UNKNOWN
状态,则此类方法将不起作用。seek
考虑创建新的 MediaPlayer,而不是重用它们
与其重复使用 MediaPlayer,不如在每次使用时创建一个新播放器,如果它们在重复使用后在您的平台上进入损坏状态,如您所述。但我想如果可能的话,请避免这种情况,因为每次创建新玩家时,可能需要时间和资源来为玩家做好准备。
正如提问者所指出的:
至于每次需要播放声音时创建一个新的 MediaPlayer,我尝试过,但由于我发现当用户快速输入时会导致延迟。
确保安装了正确的本机平台依赖项
JavaFX 媒体使用 GStreamer Lite 进行播放(媒体模块附带本机 gstreamer 库,您不需要安装它)。它可能对操作系统中的其他组件有一些依赖关系。例如,JavaFX 21 需要 GTK 3。但我不知道这些依赖关系可能是什么,如果不满足未知的依赖关系,你会怎么做:-)
也许可以尝试从平台的本机包类型(如 RPM 或 DEB)安装包含 Linux 平台的 JavaFX(如 Liberica Full JDK)的打包 JDK。如果正确打包,包管理器将确保任何本机依赖项都安装在平台上的正确版本上。我不知道这是否会对你产生任何影响,但可能是需要考虑的事情。
评论